1 package org.apache.maven.plugin.jxr;
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.IOException;
24 import java.net.URL;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Calendar;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.ResourceBundle;
33
34 import org.apache.maven.doxia.siterenderer.Renderer;
35 import org.apache.maven.jxr.JXR;
36 import org.apache.maven.jxr.JxrException;
37 import org.apache.maven.model.ReportPlugin;
38 import org.apache.maven.plugin.MojoExecutionException;
39 import org.apache.maven.plugins.annotations.Component;
40 import org.apache.maven.plugins.annotations.Parameter;
41 import org.apache.maven.project.MavenProject;
42 import org.apache.maven.reporting.AbstractMavenReport;
43 import org.apache.maven.reporting.MavenReportException;
44 import org.codehaus.plexus.languages.java.version.JavaVersion;
45 import org.codehaus.plexus.util.FileUtils;
46 import org.codehaus.plexus.util.ReaderFactory;
47 import org.codehaus.plexus.util.StringUtils;
48
49
50
51
52
53
54
55
56
57 public abstract class AbstractJxrReport
58 extends AbstractMavenReport
59 {
60 @Parameter( defaultValue = "${project}", readonly = true, required = true )
61 private MavenProject project;
62
63 @Component
64 private Renderer siteRenderer;
65
66
67
68
69
70
71 @Parameter( defaultValue = "${project.reporting.outputDirectory}", required = true )
72 private File outputDirectory;
73
74
75
76
77 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
78 private String inputEncoding;
79
80
81
82
83 @Parameter( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}" )
84 private String outputEncoding;
85
86
87
88
89 @Parameter( defaultValue = "${project.name} ${project.version} Reference" )
90 private String windowTitle;
91
92
93
94
95 @Parameter( defaultValue = "${project.name} ${project.version} Reference" )
96 private String docTitle;
97
98
99
100
101
102 @Parameter( property = "bottom", defaultValue = "Copyright © {inceptionYear}–{currentYear} {organizationName}. All rights reserved." )
103 private String bottom;
104
105
106
107
108
109
110
111 @Parameter
112 private String templateDir;
113
114
115
116
117
118 @Parameter
119 private String stylesheet;
120
121
122
123
124
125
126 @Parameter
127 private ArrayList<String> excludes;
128
129
130
131
132
133
134 @Parameter
135 private ArrayList<String> includes;
136
137
138
139
140 @Parameter( defaultValue = "${reactorProjects}", readonly = true )
141 protected List<MavenProject> reactorProjects;
142
143
144
145
146
147
148 @Parameter( property = "maven.jxr.skip", defaultValue = "false" )
149 protected boolean skip;
150
151
152
153
154
155 @Parameter( defaultValue = "true" )
156 private boolean linkJavadoc;
157
158
159
160
161
162
163 @Parameter( property = "javadocVersion" )
164 private String javadocVersion;
165
166
167
168
169 private JavaVersion javadocTemplatesVersion;
170
171
172
173
174
175
176
177 @Override
178 protected String getOutputEncoding()
179 {
180 return ( outputEncoding == null ) ? ReaderFactory.UTF_8 : outputEncoding;
181 }
182
183
184
185
186
187
188
189 protected List<String> pruneSourceDirs( List<String> sourceDirs )
190 {
191 List<String> pruned = new ArrayList<String>( sourceDirs.size() );
192 for ( String dir : sourceDirs )
193 {
194 if ( !pruned.contains( dir ) && hasSources( new File( dir ) ) )
195 {
196 pruned.add( dir );
197 }
198 }
199 return pruned;
200 }
201
202
203
204
205 protected void init()
206 {
207
208
209 Collection<ReportPlugin> plugin = project.getReportPlugins();
210 if ( plugin != null )
211 {
212 for ( ReportPlugin reportPlugin : plugin )
213 {
214 if ( "maven-javadoc-plugin".equals( reportPlugin.getArtifactId() ) )
215 {
216 break;
217 }
218 }
219 }
220 }
221
222
223
224
225
226
227
228 private boolean hasSources( File dir )
229 {
230 if ( dir.exists() && dir.isDirectory() )
231 {
232 for ( File currentFile : dir.listFiles() )
233 {
234 if ( currentFile.isFile() )
235 {
236 if ( currentFile.getName().endsWith( ".java" ) )
237 {
238 return true;
239 }
240 }
241 else
242 {
243 if ( Character.isJavaIdentifierStart( currentFile.getName().charAt( 0 ) )
244 && hasSources( currentFile ) )
245 {
246 return true;
247 }
248 }
249 }
250 }
251 return false;
252 }
253
254
255
256
257
258
259
260
261
262
263
264 private void createXref( Locale locale, String destinationDirectory, List<String> sourceDirs )
265 throws IOException, JxrException, MavenReportException
266 {
267 JXR jxr = new JXR();
268 jxr.setDest( Paths.get( destinationDirectory ) );
269 if ( StringUtils.isEmpty( inputEncoding ) )
270 {
271 String platformEncoding = System.getProperty( "file.encoding" );
272 getLog().warn( "File encoding has not been set, using platform encoding " + platformEncoding
273 + ", i.e. build is platform dependent!" );
274 }
275 jxr.setInputEncoding( inputEncoding );
276 jxr.setLocale( locale );
277 jxr.setLog( new PluginLogAdapter( getLog() ) );
278 jxr.setOutputEncoding( getOutputEncoding() );
279 jxr.setRevision( "HEAD" );
280 jxr.setJavadocLinkDir( getJavadocLocation() );
281
282 if ( excludes != null && !excludes.isEmpty() )
283 {
284 jxr.setExcludes( excludes.toArray( new String[0] ) );
285 }
286 if ( includes != null && !includes.isEmpty() )
287 {
288 jxr.setIncludes( includes.toArray( new String[0] ) );
289 }
290
291
292 ClassLoader savedTccl = Thread.currentThread().getContextClassLoader();
293 try
294 {
295 Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
296 jxr.xref( sourceDirs, getTemplateDir(), windowTitle, docTitle, getBottomText() );
297 }
298 finally
299 {
300 Thread.currentThread().setContextClassLoader( savedTccl );
301 }
302
303
304 copyRequiredResources( destinationDirectory );
305 }
306
307
308
309
310 private String getBottomText()
311 {
312 int currentYear = Calendar.getInstance().get( Calendar.YEAR );
313 String year = String.valueOf( currentYear );
314
315 String inceptionYear = project.getInceptionYear();
316
317 String theBottom = StringUtils.replace( this.bottom, "{currentYear}", year );
318
319 if ( inceptionYear != null )
320 {
321 if ( inceptionYear.equals( year ) )
322 {
323 theBottom = StringUtils.replace( theBottom, "{inceptionYear}–", "" );
324 }
325 else
326 {
327 theBottom = StringUtils.replace( theBottom, "{inceptionYear}", inceptionYear );
328 }
329 }
330 else
331 {
332 theBottom = StringUtils.replace( theBottom, "{inceptionYear}–", "" );
333 }
334
335 if ( project.getOrganization() == null )
336 {
337 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
338 }
339 else
340 {
341 if ( StringUtils.isNotEmpty( project.getOrganization().getName() ) )
342 {
343 if ( StringUtils.isNotEmpty( project.getOrganization().getUrl() ) )
344 {
345
346 theBottom =
347 StringUtils.replace( theBottom, "{organizationName}", "<a href=\""
348 + project.getOrganization().getUrl() + "\">" + project.getOrganization().getName() + "</a>" );
349
350 }
351 else
352 {
353 theBottom =
354 StringUtils.replace( theBottom, "{organizationName}", project.getOrganization().getName() );
355 }
356 }
357 else
358 {
359 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
360 }
361 }
362
363 return theBottom;
364 }
365
366
367
368
369
370
371 private void copyRequiredResources( String dir )
372 {
373 if ( StringUtils.isNotEmpty( stylesheet ) )
374 {
375 File stylesheetFile = new File( stylesheet );
376 File destStylesheetFile = new File( dir, "stylesheet.css" );
377
378 try
379 {
380 if ( stylesheetFile.isAbsolute() )
381 {
382 FileUtils.copyFile( stylesheetFile, destStylesheetFile );
383 }
384 else
385 {
386 URL stylesheetUrl = this.getClass().getClassLoader().getResource( stylesheet );
387 FileUtils.copyURLToFile( stylesheetUrl, destStylesheetFile );
388 }
389 }
390 catch ( IOException e )
391 {
392 getLog().warn( "An error occured while copying the stylesheet to the target directory", e );
393 }
394 }
395 else
396 {
397 if ( javadocTemplatesVersion.isAtLeast( "1.8" ) )
398 {
399 copyResources( dir, "jdk8/", "stylesheet.css" );
400 }
401 else if ( javadocTemplatesVersion.isAtLeast( "1.7" ) )
402 {
403 String[] jdk7Resources =
404 {
405 "stylesheet.css",
406 "resources/background.gif",
407 "resources/tab.gif",
408 "resources/titlebar.gif",
409 "resources/titlebar_end.gif"
410 };
411 copyResources( dir, "jdk7/", jdk7Resources );
412 }
413 else if ( javadocTemplatesVersion.isAtLeast( "1.6" ) )
414 {
415 copyResources( dir, "jdk6/", "stylesheet.css" );
416 }
417 else if ( javadocTemplatesVersion.isAtLeast( "1.4" ) )
418 {
419 copyResources( dir, "jdk4/", "stylesheet.css" );
420 }
421 else
422 {
423
424 copyResources( dir, "", "stylesheet.css" );
425 }
426 }
427 }
428
429
430
431
432
433
434
435
436 private void copyResources( String dir, String sourceFolder, String... files )
437 {
438 try
439 {
440 for ( String file : files )
441 {
442 URL resourceUrl = this.getClass().getClassLoader().getResource( sourceFolder + file );
443 File destResourceFile = new File( dir, file );
444 FileUtils.copyURLToFile( resourceUrl, destResourceFile );
445 }
446 }
447 catch ( IOException e )
448 {
449 getLog().warn( "An error occured while copying the resource to the target directory", e );
450 }
451 }
452
453 @Override
454 protected Renderer getSiteRenderer()
455 {
456 return siteRenderer;
457 }
458
459 @Override
460 protected String getOutputDirectory()
461 {
462 return outputDirectory.getAbsolutePath();
463 }
464
465 @Override
466 public MavenProject getProject()
467 {
468 return project;
469 }
470
471
472
473
474
475
476
477 protected ResourceBundle getBundle( Locale locale )
478 {
479 return ResourceBundle.getBundle( "jxr-report", locale, this.getClass().getClassLoader() );
480 }
481
482
483
484
485
486 protected boolean canGenerateReport( List<String> sourceDirs )
487 {
488 boolean canGenerate = !sourceDirs.isEmpty();
489
490 if ( isAggregate() && !project.isExecutionRoot() )
491 {
492 canGenerate = false;
493 }
494 return canGenerate;
495 }
496
497
498
499
500
501
502 @Override
503 public void execute()
504 throws MojoExecutionException
505 {
506
507 if ( skip )
508 {
509 getLog().info( "Skipping JXR." );
510 return;
511 }
512
513 Locale locale = Locale.getDefault();
514 try
515 {
516 executeReport( locale );
517 }
518 catch ( MavenReportException e )
519 {
520 throw new MojoExecutionException( "Error generating JXR report", e );
521 }
522 }
523
524 @Override
525 protected void executeReport( Locale locale )
526 throws MavenReportException
527 {
528 if ( skip )
529 {
530 getLog().info( "Skipping JXR." );
531 return;
532 }
533 List<String> sourceDirs = constructSourceDirs();
534 if ( canGenerateReport( sourceDirs ) )
535 {
536
537 init();
538
539
540 setJavadocTemplatesVersion();
541
542 try
543 {
544 createXref( locale, getDestinationDirectory(), sourceDirs );
545 }
546 catch ( JxrException e )
547 {
548 throw new MavenReportException( "Error while generating the HTML source code of the project.", e );
549 }
550 catch ( IOException e )
551 {
552 throw new MavenReportException( "Error while generating the HTML source code of the project.", e );
553 }
554 }
555 }
556
557
558
559
560
561
562
563
564 private String getTemplateDir()
565 {
566
567 if ( StringUtils.isEmpty( templateDir ) )
568 {
569 if ( javadocTemplatesVersion.isAtLeast( "1.8" ) )
570 {
571 return "templates/jdk8";
572 }
573 else if ( javadocTemplatesVersion.isAtLeast( "1.7" ) )
574 {
575 return "templates/jdk7";
576 }
577 else if ( javadocTemplatesVersion.isAtLeast( "1.4" ) )
578 {
579 return "templates/jdk4";
580 }
581 else
582 {
583 getLog().warn( "Unsupported javadocVersion: " + javadocTemplatesVersion + ". Fallback to original" );
584 return "templates";
585 }
586 }
587
588 return templateDir;
589 }
590
591
592
593
594 private void setJavadocTemplatesVersion()
595 {
596 JavaVersion javaVersion = JavaVersion.JAVA_SPECIFICATION_VERSION;
597
598 if ( StringUtils.isNotEmpty( javadocVersion ) )
599 {
600 javadocTemplatesVersion = JavaVersion.parse( javadocVersion );
601 }
602 else
603 {
604 javadocTemplatesVersion = javaVersion;
605 }
606 }
607
608
609
610
611
612
613 protected List<String> constructSourceDirs()
614 {
615 List<String> sourceDirs = new ArrayList<String>( getSourceRoots() );
616 if ( isAggregate() )
617 {
618 for ( MavenProject project : reactorProjects )
619 {
620 if ( "java".equals( project.getArtifact().getArtifactHandler().getLanguage() ) )
621 {
622 sourceDirs.addAll( getSourceRoots( project ) );
623 }
624 }
625 }
626
627 sourceDirs = pruneSourceDirs( sourceDirs );
628 return sourceDirs;
629 }
630
631 @Override
632 public boolean canGenerateReport()
633 {
634 return canGenerateReport( constructSourceDirs() );
635 }
636
637 @Override
638 public boolean isExternalReport()
639 {
640 return true;
641 }
642
643
644
645
646 private Path getJavadocLocation()
647 throws IOException
648 {
649 Path location = null;
650 if ( linkJavadoc )
651 {
652
653
654 if ( getJavadocDir().exists() )
655 {
656
657 location = getJavadocDir().toPath().toAbsolutePath();
658 }
659 else
660 {
661
662
663
664 String stagingDirectory = System.getProperty( "stagingDirectory" );
665
666 if ( StringUtils.isNotEmpty( stagingDirectory ) )
667 {
668 String javadocDestDir = getJavadocDir().getName();
669 boolean javadocAggregate = JxrReportUtil.isJavadocAggregated( project );
670 String structureProject = JxrReportUtil.getStructure( project, false );
671
672 if ( isAggregate() && javadocAggregate )
673 {
674 location = Paths.get( stagingDirectory, structureProject, javadocDestDir );
675 }
676 if ( !isAggregate() && javadocAggregate )
677 {
678 location = Paths.get( stagingDirectory, javadocDestDir );
679
680 String hierarchy = project.getName();
681
682 MavenProject parent = project.getParent();
683 while ( parent != null )
684 {
685 hierarchy = parent.getName();
686 parent = parent.getParent();
687 }
688 location = Paths.get( stagingDirectory, hierarchy, javadocDestDir );
689 }
690 if ( isAggregate() && !javadocAggregate )
691 {
692 getLog().warn( "The JXR plugin is configured to build an aggregated report at the root, "
693 + "not the Javadoc plugin." );
694 }
695 if ( !isAggregate() && !javadocAggregate )
696 {
697 location = Paths.get( stagingDirectory, structureProject, javadocDestDir );
698 }
699 }
700 else
701 {
702 location = getJavadocDir().toPath();
703 }
704 }
705
706 if ( location == null )
707 {
708 getLog().warn( "Unable to locate Javadoc to link to - DISABLED" );
709 }
710 }
711
712 return location;
713 }
714
715
716
717
718
719
720 protected abstract String getDestinationDirectory();
721
722
723
724
725
726
727 protected abstract List<String> getSourceRoots();
728
729
730
731
732
733
734
735
736 protected abstract List<String> getSourceRoots( MavenProject project );
737
738
739
740
741
742
743 protected abstract File getJavadocDir();
744
745
746
747
748
749
750 protected boolean isAggregate()
751 {
752 return false;
753 }
754 }