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