1 package org.codehaus.mojo.taglist;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStreamWriter;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.ResourceBundle;
32 import java.util.concurrent.atomic.AtomicReference;
33
34 import org.apache.maven.doxia.siterenderer.Renderer;
35 import org.apache.maven.model.ReportPlugin;
36 import org.apache.maven.plugins.annotations.Mojo;
37 import org.apache.maven.plugins.annotations.Parameter;
38 import org.apache.maven.plugins.annotations.ResolutionScope;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.reporting.AbstractMavenReport;
41 import org.apache.maven.reporting.MavenReportException;
42 import org.apache.maven.shared.utils.io.FileUtils;
43 import org.codehaus.mojo.taglist.beans.FileReport;
44 import org.codehaus.mojo.taglist.beans.TagReport;
45 import org.codehaus.mojo.taglist.output.TagListXMLComment;
46 import org.codehaus.mojo.taglist.output.TagListXMLFile;
47 import org.codehaus.mojo.taglist.output.TagListXMLReport;
48 import org.codehaus.mojo.taglist.output.TagListXMLTag;
49 import org.codehaus.mojo.taglist.output.io.xpp3.TaglistOutputXpp3Writer;
50 import org.codehaus.mojo.taglist.tags.AbsTag;
51 import org.codehaus.mojo.taglist.tags.InvalidTagException;
52 import org.codehaus.mojo.taglist.tags.TagClass;
53 import org.codehaus.mojo.taglist.tags.TagFactory;
54 import org.codehaus.plexus.util.IOUtil;
55 import org.codehaus.plexus.util.PathTool;
56 import org.codehaus.plexus.util.StringUtils;
57
58
59
60
61
62
63 @Mojo( name="taglist", requiresDependencyResolution = ResolutionScope.COMPILE)
64 public class TagListReport
65 extends AbstractMavenReport
66 {
67
68
69
70
71
72 @Parameter( property = "sourceFileLocale", defaultValue = "en" )
73 private String sourceFileLocale;
74
75
76 private static final String DEFAULT_LOCALE = "en";
77
78
79
80
81
82
83 @Parameter( defaultValue = "**/*.java" )
84 private String[] includes;
85
86
87
88
89
90
91 @Parameter( defaultValue = "" )
92 private String[] excludes;
93
94
95
96
97
98
99 @Parameter( defaultValue = "${project.build.directory}/taglist", required = true )
100 private File xmlOutputDirectory;
101
102
103
104
105
106
107 @Parameter( defaultValue = "true" )
108 private boolean multipleLineComments;
109
110
111
112
113 @Parameter( defaultValue = "true" )
114 private boolean emptyComments;
115
116
117
118
119
120 @Parameter( defaultValue = "true", property = "taglists.linkXRef")
121 private boolean linkXRef;
122
123
124
125
126 @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref", property = "taglists.xrefLocation" )
127 private File xrefLocation;
128
129
130
131
132 @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref-test", property = "taglists.testXrefLocation" )
133 private File testXrefLocation;
134
135
136
137
138 @Parameter( readonly = true, defaultValue = "${reactorProjects}" )
139 private List reactorProjects;
140
141
142
143
144 @Parameter( defaultValue = "false", property = "taglists.aggregate" )
145 private boolean aggregate;
146
147
148
149
150 private Locale currentLocale;
151
152
153
154
155
156
157 @Parameter( defaultValue = "false" )
158 private boolean showEmptyDetails;
159
160
161
162
163
164
165 @Parameter( defaultValue = "false" )
166 private boolean skipTestSources;
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 @Parameter
192 private org.codehaus.mojo.taglist.options.TagListOptions tagListOptions;
193
194 private String[] tags;
195
196 private AtomicReference<List> sourceDirs = new AtomicReference<>();
197
198
199
200
201
202
203 protected void executeReport( Locale locale )
204 throws MavenReportException
205 {
206 this.currentLocale = locale;
207
208
209 if ( ( tagListOptions == null || tagListOptions.getTagClasses().size() == 0 ) )
210 {
211 tags = new String[] { "@todo", "TODO" };
212 }
213
214 if ( StringUtils.isEmpty( getInputEncoding() ) )
215 {
216 getLog().warn(
217 "File encoding has not been set, using platform encoding "
218 + System.getProperty( "file.encoding" ) + ", i.e. build is platform dependent!" );
219 }
220
221
222 List<TagClass> tagClasses = new ArrayList<>();
223
224
225 if ( tags != null && tags.length > 0 )
226 {
227 getLog().warn( "Using legacy tag format. This is not recommended." );
228 for ( int i = 0; i < tags.length; i++ )
229 {
230 TagClass tc = new TagClass( tags[i] );
231 try
232 {
233 AbsTag newTag = TagFactory.createTag( "exact", tags[i] );
234 tc.addTag( newTag );
235
236 tagClasses.add( tc );
237 }
238 catch ( InvalidTagException e )
239 {
240
241 getLog().error( "Invalid tag type used. tag type: exact" );
242 }
243 }
244 }
245
246
247 if ( tagListOptions != null && tagListOptions.getTagClasses().size() > 0 )
248 {
249
250 Iterator classIter = tagListOptions.getTagClasses().iterator();
251 while ( classIter.hasNext() )
252 {
253 org.codehaus.mojo.taglist.options.TagClass tcOption =
254 (org.codehaus.mojo.taglist.options.TagClass) classIter.next();
255
256
257 TagClass tc = new TagClass( tcOption.getDisplayName() );
258
259
260 Iterator tagIter = tcOption.getTags().iterator();
261 while ( tagIter.hasNext() )
262 {
263 org.codehaus.mojo.taglist.options.Tag tagOption =
264 (org.codehaus.mojo.taglist.options.Tag) tagIter.next();
265
266
267 String matchType = tagOption.getMatchType();
268 if ( matchType == null || matchType.length() == 0 )
269 {
270 matchType = TagFactory.getDefaultTagType();
271 }
272
273 try
274 {
275
276 AbsTag newTag = TagFactory.createTag( matchType, tagOption.getMatchString() );
277 tc.addTag( newTag );
278 }
279 catch ( InvalidTagException e )
280 {
281
282 getLog().error( "Invalid tag type used. tag type: " + matchType );
283 }
284 }
285
286
287 tagClasses.add( tc );
288 }
289 }
290
291
292 FileAnalyser fileAnalyser = new FileAnalyser( this, tagClasses );
293 Collection tagReports = fileAnalyser.execute();
294
295
296 ReportGenerator generator = new ReportGenerator( this, tagReports );
297 if ( linkXRef )
298 {
299 String relativePath = getRelativePath( xrefLocation );
300 if ( xrefLocation.exists() )
301 {
302
303 generator.setXrefLocation( relativePath );
304 generator.setTestXrefLocation( getRelativePath( testXrefLocation ) );
305 }
306 else
307 {
308
309 for ( Iterator reports = getProject().getReportPlugins().iterator(); reports.hasNext(); )
310 {
311 ReportPlugin report = (ReportPlugin) reports.next();
312
313 String artifactId = report.getArtifactId();
314 if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
315 {
316 getLog().error(
317 "Taglist plugin MUST be executed after the JXR plugin."
318 + " No links to xref were generated." );
319 }
320 }
321 }
322
323 if ( generator.getXrefLocation() == null )
324 {
325 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
326 }
327 }
328 generator.generateReport();
329
330
331 generateXmlReport( tagReports );
332 }
333
334
335
336
337
338
339 private void generateXmlReport( Collection tagReports )
340 {
341 TagListXMLReport report = new TagListXMLReport();
342 report.setModelEncoding( getInputEncoding() );
343
344
345 for ( Iterator ite = tagReports.iterator(); ite.hasNext(); )
346 {
347 TagReport tagReport = (TagReport) ite.next();
348
349 TagListXMLTag tag = new TagListXMLTag();
350 tag.setName( tagReport.getTagName() );
351 tag.setCount( Integer.toString( tagReport.getTagCount() ) );
352
353
354
355 for ( Iterator fite = tagReport.getFileReports().iterator(); fite.hasNext(); )
356 {
357 FileReport fileReport = (FileReport) fite.next();
358
359 TagListXMLFile file = new TagListXMLFile();
360 file.setName( fileReport.getClassName() );
361 file.setCount( Integer.toString( fileReport.getLineIndexes().size() ) );
362
363
364
365 for ( Iterator cite = fileReport.getLineIndexes().iterator(); cite.hasNext(); )
366 {
367 Integer lineNumber = (Integer) cite.next();
368
369 TagListXMLComment comment = new TagListXMLComment();
370 comment.setLineNumber( Integer.toString( lineNumber.intValue() ) );
371 comment.setComment( fileReport.getComment( lineNumber ) );
372
373 file.addComment( comment );
374 }
375 tag.addFile( file );
376 }
377 report.addTag( tag );
378 }
379
380
381 xmlOutputDirectory.mkdirs();
382 File xmlFile = new File( xmlOutputDirectory, "taglist.xml" );
383 FileOutputStream fos = null;
384 OutputStreamWriter output = null;
385
386 try
387 {
388 fos = new FileOutputStream( xmlFile );
389 output = new OutputStreamWriter( fos, getInputEncoding());
390
391
392 TaglistOutputXpp3Writer xmlWriter = new TaglistOutputXpp3Writer();
393 xmlWriter.write( output, report );
394 }
395 catch ( Exception e )
396 {
397 getLog().warn( "Could not save taglist xml file: " + e.getMessage() );
398 }
399 finally
400 {
401 IOUtil.close( output );
402 }
403 }
404
405
406
407
408
409
410
411 private String getRelativePath( File location )
412 {
413 String relativePath =
414 PathTool.getRelativePath( getReportOutputDirectory().getAbsolutePath(), location.getAbsolutePath() );
415 if ( StringUtils.isEmpty( relativePath ) )
416 {
417 relativePath = ".";
418 }
419 relativePath = relativePath + "/" + location.getName();
420 return relativePath;
421 }
422
423
424
425
426
427
428 public boolean canGenerateReport()
429 {
430 boolean canGenerate = !getSourceDirs().isEmpty();
431 if ( aggregate && !getProject().isExecutionRoot() )
432 {
433 canGenerate = false;
434 }
435 return canGenerate;
436 }
437
438
439
440
441
442
443
444 private List pruneSourceDirs( List sourceDirectories ) throws IOException
445 {
446 List pruned = new ArrayList( sourceDirectories.size() );
447 for ( Iterator i = sourceDirectories.iterator(); i.hasNext(); )
448 {
449 String dir = (String) i.next();
450 if ( !pruned.contains( dir ) && hasSources( new File( dir ) ) )
451 {
452 pruned.add( dir );
453 }
454 }
455 return pruned;
456 }
457
458
459
460
461
462
463
464
465 private boolean hasSources( File dir ) throws IOException
466 {
467 if ( dir.exists() && dir.isDirectory() )
468 {
469 if ( !FileUtils.getFiles( dir, getIncludesCommaSeparated(), getExcludesCommaSeparated() ).isEmpty() )
470 {
471 return true;
472 }
473
474 File[] files = dir.listFiles();
475 if ( files != null )
476 {
477 for ( int i = 0; i < files.length; i++ )
478 {
479 File currentFile = files[i];
480 if ( currentFile.isDirectory() )
481 {
482 boolean hasSources = hasSources( currentFile );
483 if ( hasSources )
484 {
485 return true;
486 }
487 }
488 }
489 }
490 }
491 return false;
492 }
493
494
495
496
497
498
499 private List constructSourceDirs()
500 {
501 List dirs = new ArrayList( getProject().getCompileSourceRoots() );
502 if ( !skipTestSources )
503 {
504 dirs.addAll( getProject().getTestCompileSourceRoots() );
505 }
506
507 if ( aggregate )
508 {
509 for ( Iterator i = reactorProjects.iterator(); i.hasNext(); )
510 {
511 MavenProject reactorProject = (MavenProject) i.next();
512
513 if ( "java".equals( reactorProject.getArtifact().getArtifactHandler().getLanguage() ) )
514 {
515 dirs.addAll( reactorProject.getCompileSourceRoots() );
516 if ( !skipTestSources )
517 {
518 dirs.addAll( reactorProject.getTestCompileSourceRoots() );
519 }
520 }
521 }
522 }
523
524
525
526
527
528
529
530
531 try
532 {
533 dirs = pruneSourceDirs( dirs );
534 }
535 catch ( IOException javaIoIOException )
536 {
537 getLog().warn( "Unable to prune source dirs.", javaIoIOException );
538 }
539
540 return dirs;
541 }
542
543 protected List getSourceDirs()
544 {
545 if ( sourceDirs.get() == null )
546 {
547 sourceDirs.compareAndSet( null, constructSourceDirs() );
548 }
549
550 return sourceDirs.get();
551 }
552
553
554
555
556 String getIncludesCommaSeparated()
557 {
558 if ( includes != null )
559 {
560 return String.join( ",", includes );
561 }
562 else
563 {
564 return "";
565 }
566 }
567
568
569
570
571 String getExcludesCommaSeparated()
572 {
573 if ( excludes != null ) {
574 return String.join(",", excludes);
575 } else {
576 return "";
577 }
578 }
579
580
581
582
583
584
585 public Locale getLocale()
586 {
587
588 if ( sourceFileLocale == null )
589 {
590 sourceFileLocale = DEFAULT_LOCALE;
591 }
592 return new Locale( sourceFileLocale );
593 }
594
595
596
597
598
599
600 public boolean isMultipleLineComments()
601 {
602 return multipleLineComments;
603 }
604
605
606
607
608
609
610 public boolean isEmptyComments()
611 {
612 return emptyComments;
613 }
614
615
616
617
618
619
620 public boolean isShowEmptyDetails()
621 {
622 return showEmptyDetails;
623 }
624
625
626
627
628
629
630 protected Renderer getSiteRenderer()
631 {
632 return siteRenderer;
633 }
634
635
636
637
638
639
640 protected String getOutputDirectory()
641 {
642 return outputDirectory.getAbsolutePath();
643 }
644
645
646
647
648
649
650 protected String getXMLOutputDirectory()
651 {
652 return xmlOutputDirectory.getAbsolutePath();
653 }
654
655
656
657
658
659
660 public String getDescription( Locale locale )
661 {
662 return getBundle( locale ).getString( "report.taglist.description" );
663 }
664
665
666
667
668
669
670 public String getName( Locale locale )
671 {
672 return getBundle( locale ).getString( "report.taglist.name" );
673 }
674
675
676
677
678
679
680 public String getOutputName()
681 {
682 return "taglist";
683 }
684
685
686
687
688
689
690 public ResourceBundle getBundle()
691 {
692 return getBundle( currentLocale );
693 }
694
695
696
697
698
699
700
701 private ResourceBundle getBundle( Locale locale )
702 {
703 return ResourceBundle.getBundle( "taglist-report", locale, this.getClass().getClassLoader() );
704 }
705
706 @Override
707 protected String getInputEncoding()
708 {
709 return super.getInputEncoding();
710 }
711 }