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 sourceDirectory, String... files) {
346 try {
347 for (String file : files) {
348 URL resourceUrl = this.getClass().getClassLoader().getResource(sourceDirectory + 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 @Override
381 protected void executeReport(Locale locale) throws MavenReportException {
382
383 init();
384
385
386 setJavadocTemplatesVersion();
387
388 try {
389 createXref(locale, getDestinationDirectory(), constructSourceDirs());
390 } catch (JxrException | IOException e) {
391 throw new MavenReportException("Error while generating the HTML source code of the project.", e);
392 }
393 }
394
395
396
397
398
399
400 private String getTemplateDir() {
401
402 if (templateDir == null || templateDir.isEmpty()) {
403 if (javadocTemplatesVersion.isAtLeast("1.8")) {
404 return "templates/jdk8";
405 } else if (javadocTemplatesVersion.isAtLeast("1.7")) {
406 return "templates/jdk7";
407 } else if (javadocTemplatesVersion.isAtLeast("1.4")) {
408 return "templates/jdk4";
409 } else {
410 getLog().warn("Unsupported javadocVersion: " + javadocTemplatesVersion + ". Fallback to original");
411 return "templates";
412 }
413 }
414
415 return templateDir;
416 }
417
418
419
420
421 private void setJavadocTemplatesVersion() {
422 JavaVersion javaVersion = JavaVersion.JAVA_SPECIFICATION_VERSION;
423
424 if (javadocVersion != null && !javadocVersion.isEmpty()) {
425 javadocTemplatesVersion = JavaVersion.parse(javadocVersion);
426 } else {
427 javadocTemplatesVersion = javaVersion;
428 }
429 }
430
431
432
433
434
435
436 protected List<String> constructSourceDirs() {
437 List<String> sourceDirs = new ArrayList<>(getSourceRoots());
438 if (isAggregate()) {
439 for (MavenProject project : reactorProjects) {
440 if ("java".equals(project.getArtifact().getArtifactHandler().getLanguage())) {
441 sourceDirs.addAll(getSourceRoots(project));
442 }
443 }
444 }
445
446 sourceDirs = pruneSourceDirs(sourceDirs);
447 return sourceDirs;
448 }
449
450 @Override
451 public boolean canGenerateReport() {
452 if (skip) {
453 getLog().info("Skipping JXR.");
454 return false;
455 }
456
457 if (constructSourceDirs().isEmpty()) {
458 return false;
459 }
460
461 if (isAggregate() && !project.isExecutionRoot()) {
462 return false;
463 }
464
465 return true;
466 }
467
468 @Override
469 public boolean isExternalReport() {
470 return true;
471 }
472
473
474
475
476 private Path getJavadocLocation() throws IOException {
477 Path location = null;
478 if (linkJavadoc) {
479
480
481 if (getJavadocDir().exists()) {
482
483 location = getJavadocDir().toPath().toAbsolutePath();
484 } else {
485
486
487
488 String stagingDirectory = System.getProperty("stagingDirectory");
489
490 if (stagingDirectory != null && !stagingDirectory.isEmpty()) {
491 String javadocDestDir = getJavadocDir().getName();
492 boolean javadocAggregate = JxrReportUtil.isJavadocAggregated(project);
493 String structureProject = JxrReportUtil.getStructure(project, false);
494
495 if (isAggregate() && javadocAggregate) {
496 location = Paths.get(stagingDirectory, structureProject, javadocDestDir);
497 }
498 if (!isAggregate() && javadocAggregate) {
499 location = Paths.get(stagingDirectory, javadocDestDir);
500
501 String hierarchy = project.getName();
502
503 MavenProject parent = project.getParent();
504 while (parent != null) {
505 hierarchy = parent.getName();
506 parent = parent.getParent();
507 }
508 location = Paths.get(stagingDirectory, hierarchy, javadocDestDir);
509 }
510 if (isAggregate() && !javadocAggregate) {
511 getLog().warn("The JXR plugin is configured to build an aggregated report at the root, "
512 + "not the Javadoc plugin.");
513 }
514 if (!isAggregate() && !javadocAggregate) {
515 location = Paths.get(stagingDirectory, structureProject, javadocDestDir);
516 }
517 } else {
518 location = getJavadocDir().toPath();
519 }
520 }
521
522 if (location == null) {
523 getLog().warn("Unable to locate Javadoc to link to - DISABLED");
524 }
525 }
526
527 return location;
528 }
529
530
531
532
533
534
535 protected abstract String getDestinationDirectory();
536
537
538
539
540
541
542 protected abstract List<String> getSourceRoots();
543
544
545
546
547
548
549
550
551 protected abstract List<String> getSourceRoots(MavenProject project);
552
553
554
555
556
557
558 protected abstract File getJavadocDir();
559
560
561
562
563
564
565 protected boolean isAggregate() {
566 return false;
567 }
568 }