org.openhab.tools.analysis.checkstyle.KarafFeatureCheck.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.tools.analysis.checkstyle.KarafFeatureCheck.java

Source

/**
 * Copyright (c) 2010-2018 by the respective copyright holders.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.tools.analysis.checkstyle;

import static org.openhab.tools.analysis.checkstyle.api.CheckConstants.POM_XML_FILE_NAME;
import static org.openhab.tools.analysis.checkstyle.api.CheckConstants.XML_EXTENSION;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;

import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openhab.tools.analysis.checkstyle.api.AbstractStaticCheck;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.FileText;

/**
 * Checks if a bundle is added in a Karaf feature.xml file
 *
 * @author Svilen Valkanov - Initial contribution
 */
public class KarafFeatureCheck extends AbstractStaticCheck {

    private static final String MSG_MISSING_BUNDLE_IN_FEATURE_XML = "Bundle with ID '{0}' must be added in {1}";
    private static final String BINDING_ID_PATTERN = "mvn:{0}/{1}/{2}";
    private static final String BINDING_FEATURE_EXPRESSION = "//features/feature/bundle[text()=\"{0}\"]";
    private static final String POM_ARTIFACT_ID_XPATH_EXPRESSION = "//project/artifactId/text()";
    private static final String POM_GROUP_ID_XPATH_EXPRESSION = "//project/groupId/text()";
    private static final String POM_PARENT_GROUP_ID_XPATH_EXPRESSION = "//project/parent/groupId/text()";

    private final Log logger = LogFactory.getLog(getClass());

    /**
     * Configuration property - relative path to the feature.xml file
     */
    private String featureXmlPath;

    public KarafFeatureCheck() {
        setFileExtensions(XML_EXTENSION);
    }

    public void setFeatureXmlPath(String featureXmlPath) {
        this.featureXmlPath = featureXmlPath;
    }

    @Override
    protected void processFiltered(File file, FileText fileText) throws CheckstyleException {
        if (POM_XML_FILE_NAME.equals(file.getName())) {
            String bundleId = getBundleId(fileText);
            if (bundleId == null) {
                logger.warn(this.getClass().getSimpleName()
                        + " will be skipped. Could not find Maven group ID (parent group ID) or artifact ID in "
                        + file.getAbsolutePath());
                return;
            }

            String expression = MessageFormat.format(BINDING_FEATURE_EXPRESSION, bundleId);

            Path featurePath = resolveRecursively(file.toPath(), Paths.get(featureXmlPath));
            if (featurePath == null) {
                logger.warn(this.getClass().getSimpleName() + " will be skipped. Could not find file "
                        + featureXmlPath);
                return;
            }

            try {
                FileText featureFileText = new FileText(featurePath.toFile(), StandardCharsets.UTF_8.name());
                Document featureXML = parseDomDocumentFromFile(featureFileText);

                Node result = getFirstNode(featureXML, expression);

                if (result == null) {
                    log(0, MessageFormat.format(MSG_MISSING_BUNDLE_IN_FEATURE_XML, bundleId, featureXmlPath));
                }
            } catch (IOException e) {
                logger.error(this.getClass().getSimpleName() + " will be skipped.Could not read " + featureXmlPath);
            }
        }
    }

    private String getBundleId(FileText fileText) throws CheckstyleException {
        Document featureXML = parseDomDocumentFromFile(fileText);

        Node artifactId = getFirstNode(featureXML, POM_ARTIFACT_ID_XPATH_EXPRESSION);
        Node groupId = getFirstNode(featureXML, POM_GROUP_ID_XPATH_EXPRESSION);
        Node parentGroupId = getFirstNode(featureXML, POM_PARENT_GROUP_ID_XPATH_EXPRESSION);

        // Maven allows us to skip adding the group ID if the parent element has group ID
        if (artifactId != null && groupId != null) {
            return MessageFormat.format(BINDING_ID_PATTERN, groupId.getNodeValue(), artifactId.getNodeValue(),
                    "${project.version}");
        } else if (artifactId != null && parentGroupId != null) {
            return MessageFormat.format(BINDING_ID_PATTERN, parentGroupId.getNodeValue(), artifactId.getNodeValue(),
                    "${project.version}");
        } else {
            return null;
        }
    }

    private Node getFirstNode(Document document, String xpathExpression) {
        try {
            XPathExpression artifactIdExpression = compileXPathExpression(xpathExpression);
            return ((NodeList) artifactIdExpression.evaluate(document, XPathConstants.NODESET)).item(0);
        } catch (CheckstyleException | XPathExpressionException e) {
            logger.error("Could not evaluate XPath expression " + xpathExpression, e);
            return null;
        }
    }

    private Path resolveRecursively(Path absolute, Path relativePath) {
        while (absolute.getNameCount() > 0) {
            absolute = absolute.getParent();
            Path resolved = absolute.resolve(relativePath);
            if (Files.exists(resolved)) {
                return resolved;
            }
        }
        return null;
    }

}