| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.maven.plugins.pmd; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| 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.pull.XmlPullParserException; |
| |
| /** |
| * Base class for mojos that check if there were any PMD violations. |
| * |
| * @param <D> type of the check, e.g. {@link org.apache.maven.plugins.pmd.model.Violation} |
| * or {@link org.apache.maven.plugins.pmd.model.Duplication}. |
| * @author <a href="mailto:brett@apache.org">Brett Porter</a> |
| * @version $Id$ |
| */ |
| public abstract class AbstractPmdViolationCheckMojo<D> extends AbstractMojo { |
| /** |
| * The location of the XML report to check, as generated by the PMD report. |
| */ |
| @Parameter(property = "project.build.directory", required = true) |
| private File targetDirectory; |
| |
| /** |
| * Whether to fail the build if the validation check fails. |
| * The properties {@code failurePriority} and {@code maxAllowedViolations} control |
| * under which conditions exactly the build should be failed. |
| */ |
| @Parameter(property = "pmd.failOnViolation", defaultValue = "true", required = true) |
| protected boolean failOnViolation; |
| |
| /** |
| * Whether to build an aggregated report at the root, or build individual reports. |
| * |
| * @since 2.2 |
| * @deprecated since 3.15.0 Use the goal <code>pmd:aggregate-check</code> or |
| * <code>pmd:aggregate-cpd-check</code> instead. |
| */ |
| @Parameter(property = "aggregate", defaultValue = "false") |
| @Deprecated |
| protected boolean aggregate; |
| |
| /** |
| * Print details of check failures to build output. |
| */ |
| @Parameter(property = "pmd.verbose", defaultValue = "false") |
| private boolean verbose; |
| |
| /** |
| * Print details of errors that cause build failure |
| * |
| * @since 3.0 |
| */ |
| @Parameter(property = "pmd.printFailingErrors", defaultValue = "false") |
| private boolean printFailingErrors; |
| |
| /** |
| * File that lists classes and rules to be excluded from failures. |
| * For PMD, this is a properties file. For CPD, this |
| * is a text file that contains comma-separated lists of classes |
| * that are allowed to duplicate. |
| * |
| * @since 3.0 |
| */ |
| @Parameter(property = "pmd.excludeFromFailureFile", defaultValue = "") |
| private String excludeFromFailureFile; |
| |
| /** |
| * The maximum number of failures allowed before execution fails. |
| * Used in conjunction with {@code failOnViolation=true} and utilizes {@code failurePriority}. |
| * This value has no meaning if {@code failOnViolation=false}. |
| * If the number of failures is greater than this number, the build will be failed. |
| * If the number of failures is less than or equal to this value, |
| * then the build will not be failed. |
| * |
| * @since 3.10.0 |
| */ |
| @Parameter(property = "pmd.maxAllowedViolations", defaultValue = "0") |
| private int maxAllowedViolations; |
| |
| /** Helper to exclude violations from the result. */ |
| private final ExcludeFromFile<D> excludeFromFile; |
| |
| /** |
| * Initialize this abstact check mojo by giving the correct ExcludeFromFile helper. |
| * @param excludeFromFile the needed helper, for the specific violation type |
| */ |
| protected AbstractPmdViolationCheckMojo(ExcludeFromFile<D> excludeFromFile) { |
| this.excludeFromFile = excludeFromFile; |
| } |
| |
| /** |
| * The project to analyze. |
| */ |
| @Parameter(defaultValue = "${project}", readonly = true, required = true) |
| protected MavenProject project; |
| |
| protected void executeCheck( |
| final String filename, final String tagName, final String key, final int failurePriority) |
| throws MojoFailureException, MojoExecutionException { |
| if (aggregate && !project.isExecutionRoot()) { |
| return; |
| } |
| |
| if (!isAggregator() && "pom".equalsIgnoreCase(project.getPackaging())) { |
| return; |
| } |
| |
| excludeFromFile.loadExcludeFromFailuresData(excludeFromFailureFile); |
| final File outputFile = new File(targetDirectory, filename); |
| |
| if (outputFile.exists()) { |
| getLog().info("PMD version: " + AbstractPmdReport.getPmdVersion()); |
| |
| try { |
| final ViolationDetails<D> violations = getViolations(outputFile, failurePriority); |
| |
| final List<D> failures = violations.getFailureDetails(); |
| final List<D> warnings = violations.getWarningDetails(); |
| |
| if (verbose) { |
| printErrors(failures, warnings); |
| } |
| |
| final int failureCount = failures.size(); |
| final int warningCount = warnings.size(); |
| |
| final String message = getMessage(failureCount, warningCount, key, outputFile); |
| |
| getLog().debug("PMD failureCount: " + failureCount + ", warningCount: " + warningCount); |
| |
| if (failureCount > getMaxAllowedViolations() && isFailOnViolation()) { |
| throw new MojoFailureException(message); |
| } |
| |
| this.getLog().info(message); |
| |
| if (failureCount > 0 && isFailOnViolation() && failureCount <= getMaxAllowedViolations()) { |
| this.getLog() |
| .info("The build is not failed, since " + getMaxAllowedViolations() |
| + " violations are allowed (maxAllowedViolations)."); |
| } |
| } catch (final IOException | XmlPullParserException e) { |
| throw new MojoExecutionException("Unable to read PMD results xml: " + outputFile.getAbsolutePath(), e); |
| } |
| } else { |
| throw new MojoFailureException("Unable to perform check, " + "unable to find " + outputFile); |
| } |
| } |
| |
| /** |
| * Method for collecting the violations found by the PMD tool |
| * |
| * @param analysisFile |
| * @param failurePriority |
| * @return an int that specifies the number of violations found |
| * @throws XmlPullParserException |
| * @throws IOException |
| */ |
| private ViolationDetails<D> getViolations(final File analysisFile, final int failurePriority) |
| throws XmlPullParserException, IOException { |
| final List<D> failures = new ArrayList<>(); |
| final List<D> warnings = new ArrayList<>(); |
| |
| final List<D> violations = getErrorDetails(analysisFile); |
| |
| for (final D violation : violations) { |
| final int priority = getPriority(violation); |
| if (priority <= failurePriority && !excludeFromFile.isExcludedFromFailure(violation)) { |
| failures.add(violation); |
| if (printFailingErrors) { |
| printError(violation, "Failure"); |
| } |
| } else { |
| warnings.add(violation); |
| } |
| } |
| |
| final ViolationDetails<D> details = newViolationDetailsInstance(); |
| details.setFailureDetails(failures); |
| details.setWarningDetails(warnings); |
| return details; |
| } |
| |
| protected abstract int getPriority(D errorDetail); |
| |
| protected abstract ViolationDetails<D> newViolationDetailsInstance(); |
| |
| /** |
| * Prints the warnings and failures |
| * |
| * @param failures list of failures |
| * @param warnings list of warnings |
| */ |
| protected void printErrors(final List<D> failures, final List<D> warnings) { |
| for (final D warning : warnings) { |
| printError(warning, "Warning"); |
| } |
| |
| for (final D failure : failures) { |
| printError(failure, "Failure"); |
| } |
| } |
| |
| /** |
| * Gets the output message |
| * |
| * @param failureCount |
| * @param warningCount |
| * @param key |
| * @param outputFile |
| * @return |
| */ |
| private String getMessage(final int failureCount, final int warningCount, final String key, final File outputFile) { |
| final StringBuilder message = new StringBuilder(256); |
| if (failureCount > 0 || warningCount > 0) { |
| if (failureCount > 0) { |
| message.append("You have ") |
| .append(failureCount) |
| .append(" ") |
| .append(key) |
| .append(failureCount > 1 ? "s" : ""); |
| } |
| |
| if (warningCount > 0) { |
| if (failureCount > 0) { |
| message.append(" and "); |
| } else { |
| message.append("You have "); |
| } |
| message.append(warningCount).append(" warning").append(warningCount > 1 ? "s" : ""); |
| } |
| |
| message.append(". For more details see: ").append(outputFile.getAbsolutePath()); |
| } |
| return message.toString(); |
| } |
| |
| /** |
| * Formats the failure details and prints them as an INFO message |
| * |
| * @param item either a {@link org.apache.maven.plugins.pmd.model.Violation} from PMD |
| * or a {@link org.apache.maven.plugins.pmd.model.Duplication} from CPD |
| * @param severity the found issue is prefixed with the given severity, usually "Warning" or "Failure". |
| */ |
| protected abstract void printError(D item, String severity); |
| |
| /** |
| * Gets the attributes and text for the violation tag and puts them in a HashMap |
| * |
| * @param analysisFile the xml output from PMD or CPD |
| * @return all PMD {@link org.apache.maven.plugins.pmd.model.Violation}s |
| * or CPD {@link org.apache.maven.plugins.pmd.model.Duplication}s. |
| * @throws XmlPullParserException if the analysis file contains invalid XML |
| * @throws IOException if the analysis file could be read |
| */ |
| protected abstract List<D> getErrorDetails(File analysisFile) throws XmlPullParserException, IOException; |
| |
| public boolean isFailOnViolation() { |
| return failOnViolation; |
| } |
| |
| public Integer getMaxAllowedViolations() { |
| return maxAllowedViolations; |
| } |
| |
| protected boolean isAggregator() { |
| // returning here aggregate for backwards compatibility |
| return aggregate; |
| } |
| } |