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