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

org.jboss.maven.plugins.qstools.QSCheckerReporter Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.maven.plugins.qstools;

import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId;
import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment;
import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId;
import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin;
import static org.twdata.maven.mojoexecutor.MojoExecutor.version;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.siterenderer.Renderer;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.AbstractMavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.PlexusContainer;
import org.jboss.jdf.stacks.client.StacksClient;
import org.jboss.jdf.stacks.model.Stacks;
import org.jboss.maven.plugins.qstools.checkers.AbstractCheckstyleChecker;
import org.jboss.maven.plugins.qstools.checkers.BomVersionChecker;
import org.jboss.maven.plugins.qstools.checkers.FileHeaderChecker;
import org.jboss.maven.plugins.qstools.checkers.GroupIdChecker;
import org.jboss.maven.plugins.qstools.checkers.IllegalCharacterChecker;
import org.jboss.maven.plugins.qstools.checkers.IndentationChecker;
import org.jboss.maven.plugins.qstools.checkers.TabSpaceChecker;

import edu.emory.mathcs.backport.java.util.Collections;

/**
 * 
 * Generate the Quickstart reports with all Checks
 * 
 * @author Rafael Benevides
 * 
 */
@Mojo(name = "check", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true, threadSafe = true, aggregator = true)
public class QSCheckerReporter extends AbstractMavenReport {

    // Default excludes = target, hidden files and directories and known libraries
    private static final String DEFAULT_EXCLUDES = "**/target/**, **/.*/*.*, .*, " +
        "**/jquery*, **/cordova*, **/angular*, **/qunit*, **/backbone*, **/lodash*, **/modernizr*, **/yepnope*, **/README.html, ";

    public static final String GROUPID = "qstools.groupId";

    @Component
    private PlexusContainer container;

    @Component
    private Renderer siteRenderer;

    @Component
    private MavenProject mavenProject;

    @Component
    private BuildPluginManager pluginManager;

    @Component
    private MavenSession mavenSession;

    @Parameter(property = "reactorProjects", readonly = true, required = true)
    private List reactorProjects;

    /**
     * Overwrite the stacks file
     */
    @Parameter(property = "qstools.stacks.url")
    private URL stacksUrl;

    /**
     * Overwrite the groupId for {@link GroupIdChecker}
     */
    @Parameter(property = GROUPID, defaultValue = "org.jboss.as.quickstarts")
    private String groupId;

    /**
     * Add the file contents as excluded checking
     */
    @Parameter(property = "qstools.excludes.file")
    private File excludesFile;

    /**
     * Excludes some files from being verified by {@link FileHeaderChecker}, {@link IllegalCharacterChecker},
     * {@link IndentationChecker} and {@link TabSpaceChecker}
     */
    @Parameter(property = "qstools.excludes")
    private String excludesExpression;

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.MavenReport#getDescription(java.util.Locale)
     */
    @Override
    public String getDescription(Locale locale) {
        return "Quickstarts violations";
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.MavenReport#getName(java.util.Locale)
     */
    @Override
    public String getName(Locale locale) {
        return "Quickstarts Checker Report";
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.MavenReport#getOutputName()
     */
    @Override
    public String getOutputName() {
        return "qschecker";
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.AbstractMavenReport#getSiteRenderer()
     */
    @Override
    protected Renderer getSiteRenderer() {
        return siteRenderer;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.AbstractMavenReport#getOutputDirectory()
     */
    @Override
    protected String getOutputDirectory() {
        return mavenProject.getModel().getReporting().getOutputDirectory();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.AbstractMavenReport#getProject()
     */
    @Override
    protected MavenProject getProject() {
        return mavenProject;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.maven.reporting.AbstractMavenReport#executeReport(java.util.Locale)
     */
    @Override
    protected void executeReport(Locale locale) throws MavenReportException {
        try {
            configureParameters();
            configureStacks();
            executeJXRAndSitePlugins();

            List checkersFound = container.lookupList(QSChecker.class);
            // sort the checkers
            List checkers = new ArrayList(checkersFound);
            Collections.sort(checkers, new Comparator() {

                @Override
                public int compare(QSChecker o1, QSChecker o2) {
                    return o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
                }
            });

            Map> globalFilesViolations = new TreeMap>();
            for (QSChecker checker : checkers) {
                getLog().info("Running Checker: " + checker.getClass().getSimpleName());
                checker.resetViolationsQtd();
                Map> checkerViolations = checker.check(mavenProject, mavenSession, reactorProjects, getLog());
                addCheckerViolationsToGlobalFilesViolations(globalFilesViolations, checkerViolations);
            }
            startReport(checkers, locale);
            doFileSummary(globalFilesViolations);
            doFileReports(globalFilesViolations);
            // Display both the file name and a link for browser access
            String reportName = mavenProject.getModel().getReporting().getOutputDirectory() + File.separator + getOutputName() + ".html";
            String msg = "Your report is ready at %1$s \n       " +
                "You can access the report using Chrome or Firefox at the following URL: \n" +
                "            file://%1$s";
            getLog().info(String.format(msg, reportName));
        } catch (Exception e) {
            throw new MavenReportException(e.getMessage(), e);
        }
        endReport();

    }

    /**
     * @throws IOException if excludesFile doesn't exists
     * 
     */
    private void configureParameters() throws IOException {
        container.getContext().put(GROUPID, groupId);
        String excludes = DEFAULT_EXCLUDES + (excludesExpression == null ? "" : excludesExpression);
        if (excludesFile != null) {
            excludes = readExcludesFromFile() + ", " + excludes;
        }
        container.getContext().put(AbstractCheckstyleChecker.EXCLUDES, excludes);
        getLog().info("The following files will be ignored: " + excludes);
    }

    /**
     * @return file content split with ', '
     * @throws IOException if excludesFile doesn't exists
     */
    private String readExcludesFromFile() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(excludesFile));
        StringBuilder sb = new StringBuilder();
        try {
            while (br.ready()) {
                sb.append(br.readLine());
                sb.append(", ");
            }
        } catch (Exception e) {
            br.close();
        }
        return sb.toString();
    }

    /**
     * Check if a Custom Stacks URL was informed and configure Stacks client
     * 
     */
    private void configureStacks() {
        StacksClient stacksClient = new StacksClient();
        if (stacksUrl != null) {
            stacksClient.getActualConfiguration().setUrl(stacksUrl);
        }
        getLog().info("Using the following Stacks YML file: " + stacksClient.getActualConfiguration().getUrl());
        Stacks stacks = stacksClient.getStacks();
        container.getContext().put(BomVersionChecker.STACKS, stacks);
    }

    /**
     * Add all violations found by a Checker to tha global File Violation
     * 
     * @param filesViolations
     * @param checkerViolations
     */
    private void addCheckerViolationsToGlobalFilesViolations(Map> filesViolations, Map> checkerViolations) {
        for (String file : checkerViolations.keySet()) {
            List ckviolations = checkerViolations.get(file);
            if (filesViolations.get(file) == null) {
                getLog().debug("New violations for file: " + file);
                filesViolations.put(file, new ArrayList());
            }
            filesViolations.get(file).addAll(ckviolations);
        }
    }

    /**
     * @throws MojoExecutionException
     * 
     */
    private void executeJXRAndSitePlugins() throws MojoExecutionException {
        // Execute JXR Plugin
        executeMojo(plugin(groupId("org.apache.maven.plugins"), artifactId("maven-jxr-plugin"), version("2.3")), goal("aggregate"), configuration(),
            executionEnvironment(mavenProject, mavenSession, pluginManager));

        // Execute JXR Plugin for test sources
        executeMojo(plugin(groupId("org.apache.maven.plugins"), artifactId("maven-jxr-plugin"), version("2.3")), goal("test-aggregate"), configuration(),
            executionEnvironment(mavenProject, mavenSession, pluginManager));

        // Execute Site Plugin
        executeMojo(plugin(groupId("org.apache.maven.plugins"), artifactId("maven-site-plugin"), version("3.2")), goal("site"), configuration(),
            executionEnvironment(mavenProject, mavenSession, pluginManager));
    }

    /**
     * Prints a File and each violations it have.
     * 
     * @param fileViolations
     */
    private void doFileReports(Map> fileViolations) {
        Sink sink = getSink();

        sink.section1(); // Start Section 1
        sink.sectionTitle1();
        sink.text("Files Violations");
        sink.sectionTitle1_();

        // File Sections
        for (String file : fileViolations.keySet()) {
            sink.anchor(file.replace('/', '.'));
            sink.anchor_();

            sink.section2(); // Section 2 start
            sink.sectionTitle2();
            sink.text(file);
            sink.sectionTitle2_();

            sink.table();
            // Headers
            sink.tableRow();
            sink.tableHeaderCell();
            sink.text("Checker");
            sink.tableHeaderCell_();

            sink.tableHeaderCell();
            sink.text("Message");
            sink.tableHeaderCell_();

            sink.tableHeaderCell();
            sink.text("Line num.");
            sink.tableHeaderCell_();
            sink.tableRow_();

            // Each file violation
            List violations = fileViolations.get(file);
            for (Violation violation : violations) {

                sink.tableRow();

                sink.tableCell();
                sink.text(violation.getSourceChecker().getSimpleName());
                sink.tableCell_();

                sink.tableCell();
                sink.text(violation.getViolationMessage());
                sink.tableCell_();

                sink.tableCell();

                // Only Java files has XREF
                String expression = "src/main/java";
                int pathIndex = file.lastIndexOf(expression);
                if (pathIndex > 0) {
                    String path = file.substring(pathIndex + expression.length()).replaceAll("\\.java$", ".html");
                    File xrefSource = new File(mavenProject.getModel().getReporting().getOutputDirectory() + "/xref/" + path);
                    if (xrefSource.exists()) {
                        String linelink = xrefSource.getAbsolutePath() + "#" + violation.getLineNumber();
                        sink.link(linelink);
                    }
                }
                expression = "src/test/java";
                pathIndex = file.lastIndexOf(expression);
                if (pathIndex > 0) {
                    String path = file.substring(pathIndex + expression.length()).replaceAll("\\.java$", ".html");
                    File xrefTestSource = new File(mavenProject.getModel().getReporting().getOutputDirectory() + "/xref-test/" + path);
                    if (xrefTestSource.exists()) {
                        String linelink = xrefTestSource.getAbsolutePath() + "#" + violation.getLineNumber();
                        sink.link(linelink);
                    }
                }
                sink.text(String.valueOf(violation.getLineNumber()));
                sink.link_();
                sink.tableCell_();

                sink.tableRow_();
            }
            sink.table_();
            sink.section2_(); // End Section 2
        }
        sink.section1_(); // End Section 1
    }

    /**
     * Prints file summary
     * 
     * @param filesViolations
     */
    private void doFileSummary(Map> filesViolations) {
        Sink sink = getSink();
        sink.section1(); // Start Section 1
        sink.sectionTitle1();
        sink.text("Files");
        sink.sectionTitle2_();

        sink.table();
        // Headers
        sink.tableRow();
        sink.tableHeaderCell();
        sink.text("File");
        sink.tableHeaderCell_();

        sink.tableHeaderCell();
        sink.text("Violations qtd.");
        sink.tableHeaderCell_();
        sink.tableRow();

        for (String file : filesViolations.keySet()) {
            sink.tableRow();
            sink.tableCell();
            sink.link("#" + file.replace('/', '.'));
            sink.text(file);
            sink.link_();
            sink.tableCell_();

            sink.tableCell();
            sink.text(String.valueOf(filesViolations.get(file).size()));
            sink.tableCell_();
            sink.tableRow();

        }
        sink.table_();
        sink.section1_(); // End Section 1

    }

    /**
     * Start the Reporter HTML
     * 
     * @param checkers
     * 
     * @param locale
     * @param sink
     * 
     */
    private void startReport(List checkers, Locale locale) {
        Sink sink = getSink();
        sink.head();
        sink.title();
        sink.text(getName(locale));
        sink.title_();
        sink.head_();
        sink.body();

        sink.section1(); // Section 1 Start
        sink.sectionTitle1();
        sink.text("Quickstart Check Results");
        sink.sectionTitle1_();

        sink.text("The following checkers were used: ");
        sink.table();
        // Headers
        sink.tableRow();
        sink.tableHeaderCell();
        sink.text("Checker");
        sink.tableHeaderCell_();

        sink.tableHeaderCell();
        sink.text("Description");
        sink.tableHeaderCell_();

        sink.tableHeaderCell();
        sink.text("Violations qtd.");
        sink.tableHeaderCell_();
        sink.tableRow();

        for (QSChecker checker : checkers) {
            sink.tableRow();
            sink.tableCell();
            sink.bold();
            sink.text(checker.getClass().getSimpleName());
            sink.bold_();
            sink.link_();
            sink.tableCell_();

            sink.tableCell();
            sink.text(checker.getCheckerDescription());
            sink.tableCell_();

            sink.tableCell();
            sink.text(String.valueOf(checker.getViolatonsQtd()));
            sink.tableCell_();

            sink.tableRow();
        }
        sink.table_();

        sink.section1_(); // Section 1 End
    }

    /**
     * End the HTML report
     * 
     */
    private void endReport() {
        Sink sink = getSink();
        sink.body_();
        sink.flush();
        sink.close();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy