org.artifactory.webapp.wicket.util.DescriptionExtractor.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.webapp.wicket.util.DescriptionExtractor.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2012 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.webapp.wicket.util;

import org.apache.commons.io.IOUtils;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.lang.PropertyResolver;
import org.artifactory.descriptor.Descriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import javax.xml.XMLConstants;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Iterator;

/**
 * Utility class that extracts help messages from the artifactory.xsd file given a Descriptor and a property name.
 *
 * @author Yossi Shaul
 */
public class DescriptionExtractor {
    private static final Logger log = LoggerFactory.getLogger(DescriptionExtractor.class);

    protected Document doc;

    private static DescriptionExtractor instance;

    private DescriptionExtractor() {
        try {
            doc = loadArtifactoryXsd();
        } catch (Exception e) {
            throw new RuntimeException("Error reading schema", e);
        }
    }

    public static synchronized DescriptionExtractor getInstance() {
        if (instance == null) {
            instance = new DescriptionExtractor();
        }
        return instance;
    }

    /**
     * @param descriptor   The descriptor
     * @param propertyName The property name.
     * @return The description of the given property. If no description for the input property empty string will be
     *         returned.
     * @throws IllegalArgumentException if the property not found.
     */
    public String getDescription(Descriptor descriptor, String propertyName) {
        if (descriptor == null) {
            throw new IllegalArgumentException("Descriptor must not be null");
        }

        if (propertyName == null || "".equals(propertyName)) {
            throw new IllegalArgumentException("Property name must not be null or empty");
        }

        Field field = getField(descriptor, propertyName);

        String elementName = getElementName(field);
        String complexTypeName = getComplexTypeName(field.getDeclaringClass());

        String query = buildXPathQuery(complexTypeName, elementName);
        log.debug("Executing xpath query: {}", query);
        String description = executeQuery(query);
        return description;
    }

    private Field getField(Descriptor descriptor, String propertyName) {
        try {
            return PropertyResolver.getPropertyField(propertyName, descriptor);
        } catch (WicketRuntimeException e) {
            throw new IllegalArgumentException("Property field '" + propertyName + "' not found!", e);
        }

    }

    private String getElementName(Field field) {
        // default element name is the field name
        String elementName = field.getName();

        if (field.isAnnotationPresent(XmlElementWrapper.class)) {
            XmlElementWrapper wrapper = field.getAnnotation(XmlElementWrapper.class);
            // use the element name from the annotation only if it's not the default
            if (notDefaultName(wrapper.name())) {
                elementName = wrapper.name();
            }
        } else if (field.isAnnotationPresent(XmlElement.class)) {
            XmlElement annotation = field.getAnnotation(XmlElement.class);
            // use the element name from the annotation only if it's not the default
            if (notDefaultName(annotation.name())) {
                elementName = annotation.name();
            }
        }

        return elementName;
    }

    private boolean notDefaultName(String name) {
        return !"##default".equals(name);
    }

    protected String getComplexTypeName(Class<?> declaringClass) {
        XmlType xmlType = declaringClass.getAnnotation(XmlType.class);
        return xmlType.name();
    }

    private String buildXPathQuery(String xmlType, String elementName) {
        String xpath = "/xs:schema/xs:complexType[@name='" + xmlType + "']" + "//xs:element[@name='" + elementName
                + "']" + "/xs:annotation/xs:documentation/text()";
        return xpath;
    }

    private Document loadArtifactoryXsd() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream schemaIn = this.getClass().getResourceAsStream("/artifactory.xsd");
        try {
            return builder.parse(schemaIn);
        } finally {
            IOUtils.closeQuietly(schemaIn);
        }
    }

    private String executeQuery(String query) {
        try {
            XPathFactory xFactory = XPathFactory.newInstance();
            XPath xpath = xFactory.newXPath();
            xpath.setNamespaceContext(new SchemaNamespaceContext());
            XPathExpression expr = xpath.compile(query);
            Object description = expr.evaluate(doc, XPathConstants.STRING);
            return description.toString().trim();
        } catch (XPathExpressionException e) {
            throw new RuntimeException("Failed to execute xpath query: " + query, e);
        }
    }

    public static class SchemaNamespaceContext implements NamespaceContext {
        @Override
        public String getNamespaceURI(String prefix) {
            if (prefix == null) {
                throw new IllegalArgumentException("Null prefix");
            } else if ("xs".equals(prefix)) {
                return XMLConstants.W3C_XML_SCHEMA_NS_URI;
            } else if ("xml".equals(prefix)) {
                return XMLConstants.XML_NS_URI;
            }
            return XMLConstants.NULL_NS_URI;
        }

        // This method isn't necessary for XPath processing.

        @Override
        public String getPrefix(String uri) {
            throw new UnsupportedOperationException();
        }

        // This method isn't necessary for XPath processing either.

        @Override
        public Iterator getPrefixes(String uri) {
            throw new UnsupportedOperationException();
        }
    }
}