All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.exasol.artifactreferencechecker.AbstractArtifactReferenceCheckerMojo Maven / Gradle / Ivy

package com.exasol.artifactreferencechecker;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * This class contains the abstract implementation for verify and unify.
 */
public abstract class AbstractArtifactReferenceCheckerMojo extends AbstractMojo {
    @Parameter(defaultValue = "${project}", required = true, readonly = true)
    MavenProject project;

    @Parameter(property = "exclude")
    private List excludes;

    private final FileAndLineVisitor fileAndLineVisitor;

    /**
     * Create a new instance of {@link AbstractArtifactReferenceCheckerMojo}.
     *
     */
    public AbstractArtifactReferenceCheckerMojo() {
        this.fileAndLineVisitor = getFileAndLineVisitor();
    }

    protected abstract FileAndLineVisitor getFileAndLineVisitor();

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        final List buildPlugins = this.project.getBuildPlugins();
        final Plugin assemblyPlugin = buildPlugins.stream()
                .filter(plugin -> plugin.getArtifactId().equals("maven-assembly-plugin")).findAny()
                .orElseThrow(() -> new IllegalStateException("Could not find assembly plugin."));
        final Xpp3Dom config = (Xpp3Dom) assemblyPlugin.getConfiguration();
        final String resolvedFinalName = config.getChild("finalName").getValue() + ".jar";
        getLog().info("Detected artifact name:" + resolvedFinalName);
        final String searchPattern = buildSearchPattern();
        getLog().debug("Generated pattern: " + searchPattern);
        getLog().info("Excluded files:");
        this.excludes.forEach(getLog()::info);
        matchPatternInProjectFiles(searchPattern, resolvedFinalName);
    }

    private String buildSearchPattern() {
        final String finalName = getUnresolvedFinalName().trim() + ".jar";
        final Pattern variablePattern = Pattern.compile("\\$\\{([^\\}]*)\\}");
        final Matcher matcher = variablePattern.matcher(finalName);
        return "\\Q" + matcher.replaceAll("\\\\E.*?\\\\Q") + "\\E";
    }

    private String getUnresolvedFinalName() {
        try (final FileInputStream fileIS = new FileInputStream(
                this.project.getModel().getPomFile().getAbsolutePath())) {
            final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            builderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant  
            builderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
            final DocumentBuilder builder = builderFactory.newDocumentBuilder();
            final Document xmlDocument = builder.parse(fileIS);
            final XPath xPath = XPathFactory.newInstance().newXPath();
            final String expression = "string(/project/build/plugins/plugin[artifactId=\"maven-assembly-plugin\"]/configuration/finalName)";
            return (String) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.STRING);
        } catch (final XPathExpressionException | IOException | SAXException | ParserConfigurationException exception) {
            throw new IllegalStateException("Could not find finalName in maven-assembly-plugin");
        }
    }

    private void matchPatternInProjectFiles(final String regex, final String expected)
            throws MojoExecutionException, MojoFailureException {
        final Pattern pattern = Pattern.compile(regex);
        final File projectDirectory = this.project.getModel().getProjectDirectory();
        final FileSystem fileSystem = FileSystems.getDefault();
        final List excludeMatchers = this.excludes.stream()
                .map(excludPattern -> buildGlob(excludPattern, projectDirectory)).map(fileSystem::getPathMatcher)
                .collect(Collectors.toList());
        final FileVisitor fileVisitor = new FileVisitor(this.fileAndLineVisitor, pattern, expected, excludeMatchers);
        try {
            Files.walkFileTree(projectDirectory.toPath(), fileVisitor);
        } catch (final IOException exception) {
            throw new MojoExecutionException("Could not check files.", exception);
        } catch (final ExceptionWrapper exceptionWrapper) {
            throw exceptionWrapper.getExecutionException();
        }
        fileVisitor.report();
    }

    private String buildGlob(final String excludePattern, final File projectDir) {
        if (excludePattern.startsWith("/")) {
            return "glob:" + projectDir + excludePattern;
        } else {
            return "glob:**" + excludePattern;
        }
    }

    /**
     * File Visitor that checks if the files have the correct extension.
     */
    private static class FileVisitor extends SimpleFileVisitor {
        private final FileAndLineVisitor fileAndLineVisitor;
        private final Pattern pattern;
        private final String expected;
        List extensions = List.of("java", "md");
        final List excludeMatchers;

        private FileVisitor(final FileAndLineVisitor fileAndLineVisitor, final Pattern pattern, final String expected,
                final List excludeMatchers) {
            this.fileAndLineVisitor = fileAndLineVisitor;
            this.pattern = pattern;
            this.expected = expected;
            this.excludeMatchers = excludeMatchers;
        }

        private boolean isFileIncluded(final Path path) {
            return hasCorrectEnding(path) && !isFileExcluded(path);
        }

        private boolean isFileExcluded(final Path path) {
            return this.excludeMatchers.stream().anyMatch(matcher -> matcher.matches(path));
        }

        private boolean hasCorrectEnding(final Path path) {
            return this.extensions.stream().anyMatch(extension -> path.toString().endsWith("." + extension));
        }

        @Override
        public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
            if (isFileIncluded(file)) {
                this.fileAndLineVisitor.visit(file);
                readLines(file);
                try {
                    this.fileAndLineVisitor.leave(file);
                } catch (final MojoExecutionException exception) {
                    throw new ExceptionWrapper(exception);
                }
            }
            return FileVisitResult.CONTINUE;
        }

        private void readLines(final Path file) throws IOException {
            try (final Scanner fileScanner = new Scanner(file)) {
                while (fileScanner.hasNextLine()) {
                    final String line = fileScanner.nextLine();
                    this.fileAndLineVisitor.visitLine(line, this.pattern, this.expected);
                }
            } catch (final FileNotFoundException exception) {
                throw new IllegalStateException("Could not open project file " + file.toString() + ".", exception);
            }
        }

        private void report() throws MojoFailureException {
            this.fileAndLineVisitor.report();
        }
    }

    private static class ExceptionWrapper extends RuntimeException {
        private final MojoExecutionException executionException;

        private ExceptionWrapper(final MojoExecutionException executionException) {
            this.executionException = executionException;
        }

        private MojoExecutionException getExecutionException() {
            return this.executionException;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy