1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.pmd;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugin.MojoFailureException;
29 import org.apache.maven.plugins.annotations.Parameter;
30 import org.apache.maven.project.MavenProject;
31 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
32
33
34
35
36
37
38
39
40
41 public abstract class AbstractPmdViolationCheckMojo<D> extends AbstractMojo {
42
43
44
45 @Parameter(property = "project.build.directory", required = true)
46 private File targetDirectory;
47
48
49
50
51
52
53 @Parameter(property = "pmd.failOnViolation", defaultValue = "true", required = true)
54 protected boolean failOnViolation;
55
56
57
58
59
60
61
62
63 @Parameter(property = "aggregate", defaultValue = "false")
64 @Deprecated
65 protected boolean aggregate;
66
67
68
69
70 @Parameter(property = "pmd.verbose", defaultValue = "false")
71 private boolean verbose;
72
73
74
75
76
77
78 @Parameter(property = "pmd.printFailingErrors", defaultValue = "false")
79 private boolean printFailingErrors;
80
81
82
83
84
85
86
87
88
89 @Parameter(property = "pmd.excludeFromFailureFile", defaultValue = "")
90 private String excludeFromFailureFile;
91
92
93
94
95
96
97
98
99
100
101
102 @Parameter(property = "pmd.maxAllowedViolations", defaultValue = "0")
103 private int maxAllowedViolations;
104
105
106 private final ExcludeFromFile<D> excludeFromFile;
107
108
109
110
111
112
113 protected AbstractPmdViolationCheckMojo(ExcludeFromFile<D> excludeFromFile) {
114 this.excludeFromFile = excludeFromFile;
115 }
116
117
118
119
120 @Parameter(defaultValue = "${project}", readonly = true, required = true)
121 protected MavenProject project;
122
123 protected void executeCheck(
124 final String filename, final String analyzerName, final String failureName, final int failurePriority)
125 throws MojoFailureException, MojoExecutionException {
126 if (aggregate && !project.isExecutionRoot()) {
127 return;
128 }
129
130 if (!isAggregator() && "pom".equalsIgnoreCase(project.getPackaging())) {
131 return;
132 }
133
134 excludeFromFile.loadExcludeFromFailuresData(excludeFromFailureFile);
135 final File outputFile = new File(targetDirectory, filename);
136
137 if (outputFile.exists()) {
138 try {
139 final ViolationDetails<D> violations = getViolations(outputFile, failurePriority);
140
141 final List<D> failures = violations.getFailureDetails();
142 final List<D> warnings = violations.getWarningDetails();
143
144 if (verbose) {
145 printErrors(failures, warnings);
146 }
147
148 final int failureCount = failures.size();
149 final int warningCount = warnings.size();
150
151 final String message = getMessage(failureCount, warningCount, analyzerName, failureName, outputFile);
152
153 if (failureCount > getMaxAllowedViolations() && isFailOnViolation()) {
154 throw new MojoFailureException(message);
155 }
156
157 if (!message.isEmpty()) {
158 this.getLog().warn(message);
159 }
160
161 if (failureCount > 0 && isFailOnViolation() && failureCount <= getMaxAllowedViolations()) {
162 this.getLog()
163 .info("The build has not failed because " + getMaxAllowedViolations()
164 + " violations are allowed (maxAllowedViolations).");
165 }
166 } catch (final IOException | XmlPullParserException e) {
167 throw new MojoExecutionException(
168 "Unable to read " + analyzerName + " results XML: " + outputFile.getAbsolutePath(), e);
169 }
170 } else {
171 throw new MojoFailureException("Unable to perform check, " + "unable to find " + outputFile);
172 }
173 }
174
175
176
177
178
179
180 private ViolationDetails<D> getViolations(final File analysisFile, final int failurePriority)
181 throws XmlPullParserException, IOException {
182 final List<D> failures = new ArrayList<>();
183 final List<D> warnings = new ArrayList<>();
184
185 final List<D> violations = getErrorDetails(analysisFile);
186
187 for (final D violation : violations) {
188 final int priority = getPriority(violation);
189 if (priority <= failurePriority && !excludeFromFile.isExcludedFromFailure(violation)) {
190 failures.add(violation);
191 if (printFailingErrors) {
192 printError(violation, "Failure");
193 }
194 } else {
195 warnings.add(violation);
196 }
197 }
198
199 final ViolationDetails<D> details = newViolationDetailsInstance();
200 details.setFailureDetails(failures);
201 details.setWarningDetails(warnings);
202 return details;
203 }
204
205 protected abstract int getPriority(D errorDetail);
206
207 protected abstract ViolationDetails<D> newViolationDetailsInstance();
208
209
210
211
212
213
214
215 protected void printErrors(final List<D> failures, final List<D> warnings) {
216 for (final D warning : warnings) {
217 printError(warning, "Warning");
218 }
219
220 for (final D failure : failures) {
221 printError(failure, "Failure");
222 }
223 }
224
225
226
227
228 private String getMessage(
229 final int failureCount,
230 final int warningCount,
231 final String analyzerName,
232 final String failureName,
233 final File outputFile) {
234 final StringBuilder message = new StringBuilder(256);
235 if (failureCount > 0 || warningCount > 0) {
236 if (failureCount > 0) {
237 message.append(analyzerName)
238 .append(" ")
239 .append(AbstractPmdReport.getPmdVersion())
240 .append(" has found ")
241 .append(failureCount)
242 .append(" ")
243 .append(failureName)
244 .append(failureCount > 1 ? "s" : "");
245 }
246
247 if (warningCount > 0) {
248 if (failureCount > 0) {
249 message.append(" and issued ");
250 } else {
251 message.append(analyzerName)
252 .append(" ")
253 .append(AbstractPmdReport.getPmdVersion())
254 .append(" has issued ");
255 }
256 message.append(warningCount).append(" warning").append(warningCount > 1 ? "s" : "");
257 }
258
259 message.append(". For more details see: ").append(outputFile.getAbsolutePath());
260 }
261 return message.toString();
262 }
263
264
265
266
267
268
269
270
271 protected abstract void printError(D item, String severity);
272
273
274
275
276
277
278
279
280
281
282 protected abstract List<D> getErrorDetails(File analysisFile) throws XmlPullParserException, IOException;
283
284 public boolean isFailOnViolation() {
285 return failOnViolation;
286 }
287
288 public Integer getMaxAllowedViolations() {
289 return maxAllowedViolations;
290 }
291
292 protected boolean isAggregator() {
293
294 return aggregate;
295 }
296 }