1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.jxr;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.net.URL;
24 import java.nio.file.Path;
25 import java.nio.file.Paths;
26 import java.util.ArrayList;
27 import java.util.Calendar;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.ResourceBundle;
32
33 import org.apache.maven.execution.MavenSession;
34 import org.apache.maven.jxr.JXR;
35 import org.apache.maven.jxr.JavaCodeTransform;
36 import org.apache.maven.jxr.JxrException;
37 import org.apache.maven.jxr.pacman.FileManager;
38 import org.apache.maven.jxr.pacman.PackageManager;
39 import org.apache.maven.model.ReportPlugin;
40 import org.apache.maven.plugin.MojoExecution;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.project.MavenProject;
43 import org.apache.maven.reporting.AbstractMavenReport;
44 import org.apache.maven.reporting.MavenReportException;
45 import org.codehaus.plexus.languages.java.version.JavaVersion;
46 import org.codehaus.plexus.util.FileUtils;
47 import org.codehaus.plexus.util.StringUtils;
48
49
50
51
52
53
54
55
56 public abstract class AbstractJxrReport extends AbstractMavenReport {
57
58 @Parameter(defaultValue = "${session}", readonly = true, required = true)
59 private MavenSession session;
60
61
62
63
64 @Parameter(defaultValue = "${project.name} ${project.version} Reference")
65 private String windowTitle;
66
67
68
69
70 @Parameter(defaultValue = "${project.name} ${project.version} Reference")
71 private String docTitle;
72
73
74
75
76
77 @Parameter(property = "bottom", defaultValue = "\u00A9 {inceptionYear}\u2013{currentYear} {organizationName}")
78 private String bottom;
79
80
81
82
83
84
85
86 @Parameter
87 private String templateDir;
88
89
90
91
92
93 @Parameter
94 private String stylesheet;
95
96
97
98
99
100
101 @Parameter
102 private ArrayList<String> excludes;
103
104
105
106
107
108
109 @Parameter
110 private ArrayList<String> includes;
111
112
113
114
115
116
117 @Parameter(property = "maven.jxr.skip", defaultValue = "false")
118 protected boolean skip;
119
120
121
122
123
124 @Parameter(defaultValue = "true")
125 private boolean linkJavadoc;
126
127
128
129
130
131
132 @Parameter(property = "javadocVersion")
133 private String javadocVersion;
134
135
136
137
138 private JavaVersion javadocTemplatesVersion;
139
140
141
142
143
144
145
146 protected List<String> pruneSourceDirs(List<String> sourceDirs) {
147 List<String> pruned = new ArrayList<>(sourceDirs.size());
148 for (String dir : sourceDirs) {
149 if (!pruned.contains(dir) && hasSources(new File(dir))) {
150 pruned.add(dir);
151 }
152 }
153 return pruned;
154 }
155
156
157
158
159 protected void init() {
160
161
162 if (project.getModel().getReporting() != null) {
163 for (ReportPlugin reportPlugin : Collections.unmodifiableList(
164 project.getModel().getReporting().getPlugins())) {
165 if ("maven-javadoc-plugin".equals(reportPlugin.getArtifactId())) {
166 break;
167 }
168 }
169 }
170 }
171
172
173
174
175
176
177
178 private boolean hasSources(File dir) {
179 if (dir.exists() && dir.isDirectory()) {
180 for (File currentFile : dir.listFiles()) {
181 if (currentFile.isFile()) {
182 if (currentFile.getName().endsWith(".java")) {
183 return true;
184 }
185 } else {
186 if (Character.isJavaIdentifierStart(currentFile.getName().charAt(0))
187 && hasSources(currentFile)) {
188 return true;
189 }
190 }
191 }
192 }
193 return false;
194 }
195
196
197
198
199
200
201
202
203
204
205
206 private void createXref(Locale locale, File outputDirectory, List<String> sourceDirs)
207 throws IOException, JxrException {
208 FileManager fileManager = new FileManager();
209 PackageManager packageManager = new PackageManager(fileManager);
210 JavaCodeTransform codeTransform = new JavaCodeTransform(packageManager, fileManager);
211
212 JXR jxr = new JXR(packageManager, codeTransform);
213 jxr.setDest(outputDirectory.toPath());
214 jxr.setInputEncoding(getInputEncoding());
215 jxr.setLocale(locale);
216 jxr.setOutputEncoding(getOutputEncoding());
217 jxr.setRevision("HEAD");
218 jxr.setJavadocLinkDir(constructJavadocLocation());
219
220 if (excludes != null && !excludes.isEmpty()) {
221 jxr.setExcludes(excludes.toArray(new String[0]));
222 }
223 if (includes != null && !includes.isEmpty()) {
224 jxr.setIncludes(includes.toArray(new String[0]));
225 }
226
227
228 ClassLoader savedTccl = Thread.currentThread().getContextClassLoader();
229 try {
230 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
231 jxr.xref(sourceDirs, getTemplateDir(), windowTitle, docTitle, getBottomText());
232 } finally {
233 Thread.currentThread().setContextClassLoader(savedTccl);
234 }
235
236
237 copyRequiredResources(outputDirectory);
238 }
239
240
241
242
243 private String getBottomText() {
244 int currentYear = Calendar.getInstance().get(Calendar.YEAR);
245 String year = String.valueOf(currentYear);
246
247 String inceptionYear = project.getInceptionYear();
248
249 String theBottom = StringUtils.replace(this.bottom, "{currentYear}", year);
250
251 if (inceptionYear != null) {
252 if (inceptionYear.equals(year)) {
253 theBottom = StringUtils.replace(theBottom, "{inceptionYear}\u2013", "");
254 } else {
255 theBottom = StringUtils.replace(theBottom, "{inceptionYear}", inceptionYear);
256 }
257 } else {
258 theBottom = StringUtils.replace(theBottom, "{inceptionYear}\u2013", "");
259 }
260
261 if (project.getOrganization() == null) {
262 theBottom = StringUtils.replace(theBottom, " {organizationName}", "");
263 } else {
264 if (StringUtils.isNotEmpty(project.getOrganization().getName())) {
265 if (StringUtils.isNotEmpty(project.getOrganization().getUrl())) {
266
267 theBottom = StringUtils.replace(
268 theBottom,
269 "{organizationName}",
270 "<a href=\"" + project.getOrganization().getUrl() + "\">"
271 + project.getOrganization().getName() + "</a>");
272
273 } else {
274 theBottom = StringUtils.replace(
275 theBottom,
276 "{organizationName}",
277 project.getOrganization().getName());
278 }
279 } else {
280 theBottom = StringUtils.replace(theBottom, " {organizationName}", "");
281 }
282 }
283
284 return theBottom;
285 }
286
287
288
289
290
291
292 private void copyRequiredResources(File targetDirectory) {
293 if (stylesheet != null && !stylesheet.isEmpty()) {
294 File stylesheetFile = new File(stylesheet);
295 File targetStylesheetFile = new File(targetDirectory, "stylesheet.css");
296
297 try {
298 if (stylesheetFile.isAbsolute()) {
299 FileUtils.copyFile(stylesheetFile, targetStylesheetFile);
300 } else {
301 URL stylesheetUrl = this.getClass().getClassLoader().getResource(stylesheet);
302 FileUtils.copyURLToFile(stylesheetUrl, targetStylesheetFile);
303 }
304 } catch (IOException e) {
305 getLog().warn("An error occured while copying the stylesheet to the target directory", e);
306 }
307 } else {
308 if (javadocTemplatesVersion.isAtLeast("1.8")) {
309 copyResources(targetDirectory, "jdk8/", "stylesheet.css");
310 } else if (javadocTemplatesVersion.isAtLeast("1.7")) {
311 String[] jdk7Resources = {
312 "stylesheet.css",
313 "resources/background.gif",
314 "resources/tab.gif",
315 "resources/titlebar.gif",
316 "resources/titlebar_end.gif"
317 };
318 copyResources(targetDirectory, "jdk7/", jdk7Resources);
319 } else if (javadocTemplatesVersion.isAtLeast("1.6")) {
320 copyResources(targetDirectory, "jdk6/", "stylesheet.css");
321 } else if (javadocTemplatesVersion.isAtLeast("1.4")) {
322 copyResources(targetDirectory, "jdk4/", "stylesheet.css");
323 } else {
324
325 copyResources(targetDirectory, "", "stylesheet.css");
326 }
327 }
328 }
329
330
331
332
333
334
335
336
337 private void copyResources(File targetDirectory, String sourceDirectory, String... files) {
338 try {
339 for (String file : files) {
340 URL resourceUrl = this.getClass().getClassLoader().getResource(sourceDirectory + file);
341 File targetResourceFile = new File(targetDirectory, file);
342 FileUtils.copyURLToFile(resourceUrl, targetResourceFile);
343 }
344 } catch (IOException e) {
345 getLog().warn("An error occured while copying the resource to the target directory", e);
346 }
347 }
348
349 @Override
350 protected MavenProject getProject() {
351 return project;
352 }
353
354 protected MavenSession getSession() {
355 return session;
356 }
357
358 protected List<MavenProject> getReactorProjects() {
359 return reactorProjects;
360 }
361
362 protected MojoExecution getMojoExecution() {
363 return mojoExecution;
364 }
365
366
367
368
369
370
371
372 protected ResourceBundle getBundle(Locale locale) {
373 return ResourceBundle.getBundle("jxr-report", locale, this.getClass().getClassLoader());
374 }
375
376 @Override
377 protected void executeReport(Locale locale) throws MavenReportException {
378
379 init();
380
381
382 setJavadocTemplatesVersion();
383
384 try {
385 createXref(locale, getPluginReportOutputDirectory(), constructSourceDirs());
386 } catch (JxrException | IOException e) {
387 throw new MavenReportException("Error while generating the HTML source code of the project.", e);
388 }
389 }
390
391
392
393
394
395
396 private String getTemplateDir() {
397
398 if (templateDir == null || templateDir.isEmpty()) {
399 if (javadocTemplatesVersion.isAtLeast("1.8")) {
400 return "templates/jdk8";
401 } else if (javadocTemplatesVersion.isAtLeast("1.7")) {
402 return "templates/jdk7";
403 } else if (javadocTemplatesVersion.isAtLeast("1.4")) {
404 return "templates/jdk4";
405 } else {
406 getLog().warn("Unsupported javadocVersion: " + javadocTemplatesVersion + ". Fallback to original");
407 return "templates";
408 }
409 }
410
411 return templateDir;
412 }
413
414
415
416
417 private void setJavadocTemplatesVersion() {
418 JavaVersion javaVersion = JavaVersion.JAVA_SPECIFICATION_VERSION;
419
420 if (javadocVersion != null && !javadocVersion.isEmpty()) {
421 javadocTemplatesVersion = JavaVersion.parse(javadocVersion);
422 } else {
423 javadocTemplatesVersion = javaVersion;
424 }
425 }
426
427
428
429
430
431
432 protected List<String> constructSourceDirs() {
433 List<String> sourceDirs = new ArrayList<>(getSourceRoots());
434 if (isAggregate()) {
435 for (MavenProject project : reactorProjects) {
436 if ("java".equals(project.getArtifact().getArtifactHandler().getLanguage())) {
437 sourceDirs.addAll(getSourceRoots(project));
438 }
439 }
440 }
441
442 sourceDirs = pruneSourceDirs(sourceDirs);
443 return sourceDirs;
444 }
445
446 @Override
447 public boolean canGenerateReport() {
448 if (skip) {
449 return false;
450 }
451
452 if (constructSourceDirs().isEmpty()) {
453 return false;
454 }
455
456 if (isAggregate() && !project.isExecutionRoot()) {
457 return false;
458 }
459
460 return true;
461 }
462
463 @Override
464 public boolean isExternalReport() {
465 return true;
466 }
467
468
469
470
471 private Path constructJavadocLocation() throws IOException {
472 Path location = null;
473 if (linkJavadoc) {
474
475
476 if (getJavadocLocation().exists()) {
477
478 location = getJavadocLocation().toPath().toAbsolutePath();
479 } else {
480
481
482
483 String stagingDirectory = System.getProperty("stagingDirectory");
484
485 if (stagingDirectory != null && !stagingDirectory.isEmpty()) {
486 String javadocOutputDir = getJavadocLocation().getName();
487 boolean javadocAggregate = JxrReportUtil.isJavadocAggregated(project);
488 String structureProject = JxrReportUtil.getStructure(project, false);
489
490 if (isAggregate() && javadocAggregate) {
491 location = Paths.get(stagingDirectory, structureProject, javadocOutputDir);
492 }
493 if (!isAggregate() && javadocAggregate) {
494 location = Paths.get(stagingDirectory, javadocOutputDir);
495
496 String hierarchy = project.getName();
497
498 MavenProject parent = project.getParent();
499 while (parent != null) {
500 hierarchy = parent.getName();
501 parent = parent.getParent();
502 }
503 location = Paths.get(stagingDirectory, hierarchy, javadocOutputDir);
504 }
505 if (isAggregate() && !javadocAggregate) {
506 getLog().warn("The JXR plugin is configured to build an aggregated report at the root, "
507 + "not the Javadoc plugin.");
508 }
509 if (!isAggregate() && !javadocAggregate) {
510 location = Paths.get(stagingDirectory, structureProject, javadocOutputDir);
511 }
512 } else {
513 location = getJavadocLocation().toPath();
514 }
515 }
516
517 if (location == null) {
518 getLog().warn("Unable to locate Javadoc to link to - DISABLED");
519 }
520 }
521
522 return location;
523 }
524
525
526
527
528
529
530
531 protected abstract File getPluginReportOutputDirectory();
532
533
534
535
536
537
538 protected abstract List<String> getSourceRoots();
539
540
541
542
543
544
545
546
547 protected abstract List<String> getSourceRoots(MavenProject project);
548
549
550
551
552
553
554 protected abstract File getJavadocLocation();
555
556
557
558
559
560
561 protected boolean isAggregate() {
562 return false;
563 }
564 }