1 package org.apache.maven.plugin.pmd;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6
7 import org.apache.maven.plugin.AbstractMojo;
8 import org.apache.maven.plugin.MojoExecutionException;
9
10
11
12
13
14
15
16
17
18
19 @SuppressWarnings( "all" )
20 public class HelpMojo
21 extends AbstractMojo
22 {
23
24
25
26
27
28 private boolean detail;
29
30
31
32
33
34
35 private java.lang.String goal;
36
37
38
39
40
41
42 private int lineLength;
43
44
45
46
47
48
49 private int indentSize;
50
51
52
53 public void execute()
54 throws MojoExecutionException
55 {
56 if ( lineLength <= 0 )
57 {
58 getLog().warn( "The parameter 'lineLength' should be positive, using '80' as default." );
59 lineLength = 80;
60 }
61 if ( indentSize <= 0 )
62 {
63 getLog().warn( "The parameter 'indentSize' should be positive, using '2' as default." );
64 indentSize = 2;
65 }
66
67 StringBuffer sb = new StringBuffer();
68
69 append( sb, "org.apache.maven.plugins:maven-pmd-plugin:2.7", 0 );
70 append( sb, "", 0 );
71
72 append( sb, "Maven PMD Plugin", 0 );
73 append( sb, "A Maven plugin for the PMD toolkit, that produces a report on both code rule violations and detected copy and paste fragments, as well as being able to fail the build based on these metrics.", 1 );
74 append( sb, "", 0 );
75
76 if ( goal == null || goal.length() <= 0 )
77 {
78 append( sb, "This plugin has 5 goals:", 0 );
79 append( sb, "", 0 );
80 }
81
82 if ( goal == null || goal.length() <= 0 || "check".equals( goal ) )
83 {
84 append( sb, "pmd:check", 0 );
85 append( sb, "Fail the build if there were any PMD violations in the source code.", 1 );
86 append( sb, "", 0 );
87 if ( detail )
88 {
89 append( sb, "Available parameters:", 1 );
90 append( sb, "", 0 );
91
92 append( sb, "aggregate (Default: false)", 2 );
93 append( sb, "Whether to build an aggregated report at the root, or build individual reports.", 3 );
94 append( sb, "Expression: ${aggregate}", 3 );
95 append( sb, "", 0 );
96
97 append( sb, "failOnViolation (Default: true)", 2 );
98 append( sb, "Whether to fail the build if the validation check fails.", 3 );
99 append( sb, "Required: Yes", 3 );
100 append( sb, "Expression: ${pmd.failOnViolation}", 3 );
101 append( sb, "", 0 );
102
103 append( sb, "failurePriority (Default: 5)", 2 );
104 append( sb, "What priority level to fail the build on. Failures at or above this level will stop the build. Anything below will be warnings and will be displayed in the build output if verbose=true. Note: Minimum Priority = 5 Maximum Priority = 0", 3 );
105 append( sb, "Required: Yes", 3 );
106 append( sb, "Expression: ${pmd.failurePriority}", 3 );
107 append( sb, "", 0 );
108
109 append( sb, "skip (Default: false)", 2 );
110 append( sb, "Skip the PMD checks. Most useful on the command line via \'-Dpmd.skip=true\'.", 3 );
111 append( sb, "Expression: ${pmd.skip}", 3 );
112 append( sb, "", 0 );
113
114 append( sb, "targetDirectory", 2 );
115 append( sb, "The location of the XML report to check, as generated by the PMD report.", 3 );
116 append( sb, "Required: Yes", 3 );
117 append( sb, "Expression: ${project.build.directory}", 3 );
118 append( sb, "", 0 );
119
120 append( sb, "verbose (Default: false)", 2 );
121 append( sb, "Print details of check failures to build output.", 3 );
122 append( sb, "Expression: ${pmd.verbose}", 3 );
123 append( sb, "", 0 );
124 }
125 }
126
127 if ( goal == null || goal.length() <= 0 || "cpd".equals( goal ) )
128 {
129 append( sb, "pmd:cpd", 0 );
130 append( sb, "Creates a report for PMD\'s CPD tool. See http://pmd.sourceforge.net/cpd.html for more detail.", 1 );
131 append( sb, "", 0 );
132 if ( detail )
133 {
134 append( sb, "Available parameters:", 1 );
135 append( sb, "", 0 );
136
137 append( sb, "aggregate (Default: false)", 2 );
138 append( sb, "Whether to build an aggregated report at the root, or build individual reports.", 3 );
139 append( sb, "Expression: ${aggregate}", 3 );
140 append( sb, "", 0 );
141
142 append( sb, "excludeRoots", 2 );
143 append( sb, "The project source directories that should be excluded.", 3 );
144 append( sb, "", 0 );
145
146 append( sb, "excludes", 2 );
147 append( sb, "A list of files to exclude from checking. Can contain Ant-style wildcards and double wildcards. Note that these exclusion patterns only operate on the path of a source file relative to its source root directory. In other words, files are excluded based on their package and/or class name. If you want to exclude entire source root directories, use the parameter excludeRoots instead.", 3 );
148 append( sb, "", 0 );
149
150 append( sb, "format (Default: xml)", 2 );
151 append( sb, "Set the output format type, in addition to the HTML report. Must be one of: \'none\', \'csv\', \'xml\', \'txt\' or the full class name of the PMD renderer to use. See the net.sourceforge.pmd.renderers package javadoc for available renderers. XML is required if the pmd:check goal is being used.", 3 );
152 append( sb, "Expression: ${format}", 3 );
153 append( sb, "", 0 );
154
155 append( sb, "ignoreIdentifiers (Default: false)", 2 );
156 append( sb, "Similar to ignoreLiterals but for identifiers; i.e., variable names, methods names, and so forth.", 3 );
157 append( sb, "Expression: ${cpd.ignoreIdentifiers}", 3 );
158 append( sb, "", 0 );
159
160 append( sb, "ignoreLiterals (Default: false)", 2 );
161 append( sb, "If true, CPD ignores literal value differences when evaluating a duplicate block. This means that foo=42; and foo=43; will be seen as equivalent. You may want to run PMD with this option off to start with and then switch it on to see what it turns up.", 3 );
162 append( sb, "Expression: ${cpd.ignoreLiterals}", 3 );
163 append( sb, "", 0 );
164
165 append( sb, "includes", 2 );
166 append( sb, "A list of files to include from checking. Can contain Ant-style wildcards and double wildcards. Defaults to **\\/*.java.", 3 );
167 append( sb, "", 0 );
168
169 append( sb, "includeTests (Default: false)", 2 );
170 append( sb, "Run PMD on the tests.", 3 );
171 append( sb, "", 0 );
172
173 append( sb, "linkXRef (Default: true)", 2 );
174 append( sb, "Link the violation line numbers to the source xref. Links will be created automatically if the jxr plugin is being used.", 3 );
175 append( sb, "Expression: ${linkXRef}", 3 );
176 append( sb, "", 0 );
177
178 append( sb, "minimumTokens (Default: 100)", 2 );
179 append( sb, "The minimum number of tokens that need to be duplicated before it causes a violation.", 3 );
180 append( sb, "Expression: ${minimumTokens}", 3 );
181 append( sb, "", 0 );
182
183 append( sb, "outputDirectory", 2 );
184 append( sb, "The output directory for the final HTML report. Note that this parameter is only evaluated if the goal is run directly from the command line or during the default lifecycle. If the goal is run indirectly as part of a site generation, the output directory configured in the Maven Site Plugin is used instead.", 3 );
185 append( sb, "Required: Yes", 3 );
186 append( sb, "Expression: ${project.reporting.outputDirectory}", 3 );
187 append( sb, "", 0 );
188
189 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
190 append( sb, "The file encoding when writing non-HTML reports.", 3 );
191 append( sb, "Expression: ${outputEncoding}", 3 );
192 append( sb, "", 0 );
193
194 append( sb, "skip (Default: false)", 2 );
195 append( sb, "Skip the CPD report generation. Most useful on the command line via \'-Dcpd.skip=true\'.", 3 );
196 append( sb, "Expression: ${cpd.skip}", 3 );
197 append( sb, "", 0 );
198
199 append( sb, "sourceEncoding (Default: ${project.build.sourceEncoding})", 2 );
200 append( sb, "The file encoding to use when reading the Java sources.", 3 );
201 append( sb, "Expression: ${encoding}", 3 );
202 append( sb, "", 0 );
203
204 append( sb, "targetDirectory", 2 );
205 append( sb, "The output directory for the intermediate XML report.", 3 );
206 append( sb, "Required: Yes", 3 );
207 append( sb, "Expression: ${project.build.directory}", 3 );
208 append( sb, "", 0 );
209
210 append( sb, "xrefLocation (Default: ${project.reporting.outputDirectory}/xref)", 2 );
211 append( sb, "Location of the Xrefs to link to.", 3 );
212 append( sb, "", 0 );
213
214 append( sb, "xrefTestLocation (Default: ${project.reporting.outputDirectory}/xref-test)", 2 );
215 append( sb, "Location of the Test Xrefs to link to.", 3 );
216 append( sb, "", 0 );
217 }
218 }
219
220 if ( goal == null || goal.length() <= 0 || "cpd-check".equals( goal ) )
221 {
222 append( sb, "pmd:cpd-check", 0 );
223 append( sb, "Fail the build if there were any CPD violations in the source code.", 1 );
224 append( sb, "", 0 );
225 if ( detail )
226 {
227 append( sb, "Available parameters:", 1 );
228 append( sb, "", 0 );
229
230 append( sb, "aggregate (Default: false)", 2 );
231 append( sb, "Whether to build an aggregated report at the root, or build individual reports.", 3 );
232 append( sb, "Expression: ${aggregate}", 3 );
233 append( sb, "", 0 );
234
235 append( sb, "failOnViolation (Default: true)", 2 );
236 append( sb, "Whether to fail the build if the validation check fails.", 3 );
237 append( sb, "Required: Yes", 3 );
238 append( sb, "Expression: ${pmd.failOnViolation}", 3 );
239 append( sb, "", 0 );
240
241 append( sb, "skip (Default: false)", 2 );
242 append( sb, "Skip the CPD violation checks. Most useful on the command line via \'-Dcpd.skip=true\'.", 3 );
243 append( sb, "Expression: ${cpd.skip}", 3 );
244 append( sb, "", 0 );
245
246 append( sb, "targetDirectory", 2 );
247 append( sb, "The location of the XML report to check, as generated by the PMD report.", 3 );
248 append( sb, "Required: Yes", 3 );
249 append( sb, "Expression: ${project.build.directory}", 3 );
250 append( sb, "", 0 );
251
252 append( sb, "verbose (Default: false)", 2 );
253 append( sb, "Print details of check failures to build output.", 3 );
254 append( sb, "Expression: ${pmd.verbose}", 3 );
255 append( sb, "", 0 );
256 }
257 }
258
259 if ( goal == null || goal.length() <= 0 || "help".equals( goal ) )
260 {
261 append( sb, "pmd:help", 0 );
262 append( sb, "Display help information on maven-pmd-plugin.\nCall\n\u00a0\u00a0mvn\u00a0pmd:help\u00a0-Ddetail=true\u00a0-Dgoal=<goal-name>\nto display parameter details.", 1 );
263 append( sb, "", 0 );
264 if ( detail )
265 {
266 append( sb, "Available parameters:", 1 );
267 append( sb, "", 0 );
268
269 append( sb, "detail (Default: false)", 2 );
270 append( sb, "If true, display all settable properties for each goal.", 3 );
271 append( sb, "Expression: ${detail}", 3 );
272 append( sb, "", 0 );
273
274 append( sb, "goal", 2 );
275 append( sb, "The name of the goal for which to show help. If unspecified, all goals will be displayed.", 3 );
276 append( sb, "Expression: ${goal}", 3 );
277 append( sb, "", 0 );
278
279 append( sb, "indentSize (Default: 2)", 2 );
280 append( sb, "The number of spaces per indentation level, should be positive.", 3 );
281 append( sb, "Expression: ${indentSize}", 3 );
282 append( sb, "", 0 );
283
284 append( sb, "lineLength (Default: 80)", 2 );
285 append( sb, "The maximum length of a display line, should be positive.", 3 );
286 append( sb, "Expression: ${lineLength}", 3 );
287 append( sb, "", 0 );
288 }
289 }
290
291 if ( goal == null || goal.length() <= 0 || "pmd".equals( goal ) )
292 {
293 append( sb, "pmd:pmd", 0 );
294 append( sb, "Creates a PMD report.", 1 );
295 append( sb, "", 0 );
296 if ( detail )
297 {
298 append( sb, "Available parameters:", 1 );
299 append( sb, "", 0 );
300
301 append( sb, "aggregate (Default: false)", 2 );
302 append( sb, "Whether to build an aggregated report at the root, or build individual reports.", 3 );
303 append( sb, "Expression: ${aggregate}", 3 );
304 append( sb, "", 0 );
305
306 append( sb, "excludeRoots", 2 );
307 append( sb, "The project source directories that should be excluded.", 3 );
308 append( sb, "", 0 );
309
310 append( sb, "excludes", 2 );
311 append( sb, "A list of files to exclude from checking. Can contain Ant-style wildcards and double wildcards. Note that these exclusion patterns only operate on the path of a source file relative to its source root directory. In other words, files are excluded based on their package and/or class name. If you want to exclude entire source root directories, use the parameter excludeRoots instead.", 3 );
312 append( sb, "", 0 );
313
314 append( sb, "format (Default: xml)", 2 );
315 append( sb, "Set the output format type, in addition to the HTML report. Must be one of: \'none\', \'csv\', \'xml\', \'txt\' or the full class name of the PMD renderer to use. See the net.sourceforge.pmd.renderers package javadoc for available renderers. XML is required if the pmd:check goal is being used.", 3 );
316 append( sb, "Expression: ${format}", 3 );
317 append( sb, "", 0 );
318
319 append( sb, "includes", 2 );
320 append( sb, "A list of files to include from checking. Can contain Ant-style wildcards and double wildcards. Defaults to **\\/*.java.", 3 );
321 append( sb, "", 0 );
322
323 append( sb, "includeTests (Default: false)", 2 );
324 append( sb, "Run PMD on the tests.", 3 );
325 append( sb, "", 0 );
326
327 append( sb, "linkXRef (Default: true)", 2 );
328 append( sb, "Link the violation line numbers to the source xref. Links will be created automatically if the jxr plugin is being used.", 3 );
329 append( sb, "Expression: ${linkXRef}", 3 );
330 append( sb, "", 0 );
331
332 append( sb, "minimumPriority (Default: 5)", 2 );
333 append( sb, "The rule priority threshold; rules with lower priority than this will not be evaluated.", 3 );
334 append( sb, "Expression: ${minimumPriority}", 3 );
335 append( sb, "", 0 );
336
337 append( sb, "outputDirectory", 2 );
338 append( sb, "The output directory for the final HTML report. Note that this parameter is only evaluated if the goal is run directly from the command line or during the default lifecycle. If the goal is run indirectly as part of a site generation, the output directory configured in the Maven Site Plugin is used instead.", 3 );
339 append( sb, "Required: Yes", 3 );
340 append( sb, "Expression: ${project.reporting.outputDirectory}", 3 );
341 append( sb, "", 0 );
342
343 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
344 append( sb, "The file encoding when writing non-HTML reports.", 3 );
345 append( sb, "Expression: ${outputEncoding}", 3 );
346 append( sb, "", 0 );
347
348 append( sb, "rulesets", 2 );
349 append( sb, "The PMD rulesets to use. See the Stock Rulesets for a list of some included. Since version 2.5, the ruleset \'rulesets/maven.xml\' is also available. Defaults to the basic, imports and unusedcode rulesets.", 3 );
350 append( sb, "", 0 );
351
352 append( sb, "skip (Default: false)", 2 );
353 append( sb, "Skip the PMD report generation. Most useful on the command line via \'-Dpmd.skip=true\'.", 3 );
354 append( sb, "Expression: ${pmd.skip}", 3 );
355 append( sb, "", 0 );
356
357 append( sb, "sourceEncoding (Default: ${project.build.sourceEncoding})", 2 );
358 append( sb, "The file encoding to use when reading the Java sources.", 3 );
359 append( sb, "Expression: ${encoding}", 3 );
360 append( sb, "", 0 );
361
362 append( sb, "targetDirectory", 2 );
363 append( sb, "The output directory for the intermediate XML report.", 3 );
364 append( sb, "Required: Yes", 3 );
365 append( sb, "Expression: ${project.build.directory}", 3 );
366 append( sb, "", 0 );
367
368 append( sb, "targetJdk", 2 );
369 append( sb, "The target JDK to analyze based on. Should match the target used in the compiler plugin. Valid values are currently 1.3, 1.4, 1.5 and 1.6.\nNote: support for 1.6 was added in version 2.3 of this plugin.\n", 3 );
370 append( sb, "Expression: ${targetJdk}", 3 );
371 append( sb, "", 0 );
372
373 append( sb, "xrefLocation (Default: ${project.reporting.outputDirectory}/xref)", 2 );
374 append( sb, "Location of the Xrefs to link to.", 3 );
375 append( sb, "", 0 );
376
377 append( sb, "xrefTestLocation (Default: ${project.reporting.outputDirectory}/xref-test)", 2 );
378 append( sb, "Location of the Test Xrefs to link to.", 3 );
379 append( sb, "", 0 );
380 }
381 }
382
383 if ( getLog().isInfoEnabled() )
384 {
385 getLog().info( sb.toString() );
386 }
387 }
388
389
390
391
392
393
394
395
396
397
398 private static String repeat( String str, int repeat )
399 {
400 StringBuffer buffer = new StringBuffer( repeat * str.length() );
401
402 for ( int i = 0; i < repeat; i++ )
403 {
404 buffer.append( str );
405 }
406
407 return buffer.toString();
408 }
409
410
411
412
413
414
415
416
417
418 private void append( StringBuffer sb, String description, int indent )
419 {
420 for ( Iterator it = toLines( description, indent, indentSize, lineLength ).iterator(); it.hasNext(); )
421 {
422 sb.append( it.next().toString() ).append( '\n' );
423 }
424 }
425
426
427
428
429
430
431
432
433
434
435
436 private static List toLines( String text, int indent, int indentSize, int lineLength )
437 {
438 List<String> lines = new ArrayList<String>();
439
440 String ind = repeat( "\t", indent );
441 String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
442 for ( int i = 0; i < plainLines.length; i++ )
443 {
444 toLines( lines, ind + plainLines[i], indentSize, lineLength );
445 }
446
447 return lines;
448 }
449
450
451
452
453
454
455
456
457
458 private static void toLines( List<String> lines, String line, int indentSize, int lineLength )
459 {
460 int lineIndent = getIndentLevel( line );
461 StringBuffer buf = new StringBuffer( 256 );
462 String[] tokens = line.split( " +" );
463 for ( int i = 0; i < tokens.length; i++ )
464 {
465 String token = tokens[i];
466 if ( i > 0 )
467 {
468 if ( buf.length() + token.length() >= lineLength )
469 {
470 lines.add( buf.toString() );
471 buf.setLength( 0 );
472 buf.append( repeat( " ", lineIndent * indentSize ) );
473 }
474 else
475 {
476 buf.append( ' ' );
477 }
478 }
479 for ( int j = 0; j < token.length(); j++ )
480 {
481 char c = token.charAt( j );
482 if ( c == '\t' )
483 {
484 buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
485 }
486 else if ( c == '\u00A0' )
487 {
488 buf.append( ' ' );
489 }
490 else
491 {
492 buf.append( c );
493 }
494 }
495 }
496 lines.add( buf.toString() );
497 }
498
499
500
501
502
503
504
505 private static int getIndentLevel( String line )
506 {
507 int level = 0;
508 for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
509 {
510 level++;
511 }
512 for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
513 {
514 if ( line.charAt( i ) == '\t' )
515 {
516 level++;
517 break;
518 }
519 }
520 return level;
521 }
522 }