Java tutorial
/** * 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; } }