1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.codehaus.gmavenplus.mojo;
18
19 import org.apache.maven.plugins.annotations.Parameter;
20 import org.apache.maven.shared.model.fileset.FileSet;
21 import org.apache.maven.shared.model.fileset.util.FileSetManager;
22 import org.codehaus.gmavenplus.groovyworkarounds.GroovyDocTemplateInfo;
23 import org.codehaus.gmavenplus.javaparser.LanguageLevel;
24 import org.codehaus.gmavenplus.model.IncludeClasspath;
25 import org.codehaus.gmavenplus.model.Link;
26 import org.codehaus.gmavenplus.model.Scopes;
27 import org.codehaus.gmavenplus.model.internal.Version;
28 import org.codehaus.gmavenplus.util.FileUtils;
29
30 import java.io.BufferedReader;
31 import java.io.BufferedWriter;
32 import java.io.File;
33 import java.io.IOException;
34 import java.io.InputStreamReader;
35 import java.io.OutputStreamWriter;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.net.MalformedURLException;
39 import java.nio.file.Files;
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Properties;
43
44 import static org.codehaus.gmavenplus.util.ReflectionUtils.findConstructor;
45 import static org.codehaus.gmavenplus.util.ReflectionUtils.findMethod;
46 import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeConstructor;
47 import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeMethod;
48
49
50
51
52
53
54
55
56 public abstract class AbstractGroovyDocMojo extends AbstractGroovySourcesMojo {
57
58
59
60
61 protected static final Version GROOVY_5_0_0_BETA_1 = new Version(5, 0, 0, "beta-1");
62
63
64
65
66 protected static final Version GROOVY_5_0_0_ALPHA_1 = new Version(5, 0, 0, "alpha-1");
67
68
69
70
71 protected static final Version GROOVY_4_0_27 = new Version(4, 0, 27);
72
73
74
75
76 protected static final Version GROOVY_3_0_0_ALPHA_4 = new Version(3, 0, 0, "alpha-4");
77
78
79
80
81 protected static final Version GROOVY_1_6_0_RC2 = new Version(1, 6, 0, "RC-2");
82
83
84
85
86 protected static final Version GROOVY_1_6_0_RC1 = new Version(1, 6, 0, "RC-1");
87
88
89
90
91 protected static final Version GROOVY_1_5_8 = new Version(1, 5, 8);
92
93
94
95
96 protected static final Version GROOVY_1_5_2 = new Version(1, 5, 2);
97
98
99
100
101 @Parameter(defaultValue = "Groovy Documentation")
102 protected String windowTitle;
103
104
105
106
107 @Parameter(defaultValue = "Groovy Documentation")
108 protected String docTitle;
109
110
111
112
113 @Parameter(defaultValue = "Groovy Documentation")
114 protected String footer;
115
116
117
118
119 @Parameter
120 protected LanguageLevel languageLevel;
121
122
123
124
125 @Parameter(defaultValue = "Groovy Documentation")
126 protected String header;
127
128
129
130
131 @Parameter(defaultValue = "true")
132 protected boolean displayAuthor;
133
134
135
136
137 @Parameter
138 protected File overviewFile;
139
140
141
142
143 @Parameter
144 protected File stylesheetFile;
145
146
147
148
149 @Parameter(defaultValue = "${project.build.sourceEncoding}")
150 protected String stylesheetEncoding;
151
152
153
154
155
156
157
158
159
160
161 @Parameter(defaultValue = "private")
162 protected String scope;
163
164
165
166
167
168
169 @Parameter
170 protected List<Link> links;
171
172
173
174
175
176
177 @Parameter(property = "skipGroovydoc", defaultValue = "false")
178 protected boolean skipGroovyDoc;
179
180
181
182
183
184
185
186
187
188
189
190
191 @Parameter(defaultValue = "PROJECT_ONLY")
192 protected IncludeClasspath includeClasspath;
193
194
195
196
197
198
199 @Parameter
200 protected String[] defaultDocTemplates = null;
201
202
203
204
205
206
207 @Parameter
208 protected String[] defaultPackageTemplates = null;
209
210
211
212
213
214
215 @Parameter
216 protected String[] defaultClassTemplates = null;
217
218
219
220
221
222
223
224 @Parameter
225 protected String groovyDocToolClass = null;
226
227
228
229
230
231
232
233 @Parameter
234 protected String outputToolClass = null;
235
236
237
238
239
240
241
242 @Parameter
243 protected String fileOutputToolClass = null;
244
245
246
247
248
249
250
251 @Parameter
252 protected String resourceManagerClass = null;
253
254
255
256
257
258
259
260 @Parameter
261 protected String classpathResourceManagerClass = null;
262
263
264
265
266
267
268
269
270 @Parameter
271 protected String linkArgumentClass = null;
272
273
274
275
276
277
278 @Parameter(defaultValue = "false")
279 protected boolean attachGroovyDocAnnotation;
280
281
282
283
284
285
286
287
288
289
290
291
292
293 protected synchronized void doGroovyDocGeneration(final FileSet[] sourceDirectories, final List<?> classpath, final File outputDirectory) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, MalformedURLException {
294 if (skipGroovyDoc) {
295 getLog().info("Skipping generation of GroovyDoc because ${skipGroovydoc} was set to true.");
296 return;
297 }
298
299 if (sourceDirectories == null || sourceDirectories.length == 0) {
300 getLog().info("No source directories specified for GroovyDoc generation. Skipping.");
301 return;
302 }
303
304 setupClassWrangler(classpath, includeClasspath);
305
306 classWrangler.logGroovyVersion(mojoExecution.getMojoDescriptor().getGoal());
307 logPluginClasspath();
308
309 if (!groovyVersionSupportsAction()) {
310 getLog().error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support GroovyDoc. The minimum version of Groovy required is " + minGroovyVersion + ". Skipping GroovyDoc generation.");
311 return;
312 }
313 if (groovyIs(GROOVY_1_6_0_RC1) || groovyIs(GROOVY_1_5_8)) {
314
315 getLog().warn("Groovy " + GROOVY_1_5_8 + " and " + GROOVY_1_6_0_RC1 + " are blacklisted from the supported GroovyDoc versions because of their dependency on Ant. Skipping GroovyDoc generation.");
316 return;
317 }
318
319
320 Class<?> groovyDocToolClass = classWrangler.getClass(this.groovyDocToolClass == null ? "org.codehaus.groovy.tools.groovydoc.GroovyDocTool" : this.groovyDocToolClass);
321 Class<?> outputToolClass = classWrangler.getClass(this.outputToolClass == null ? "org.codehaus.groovy.tools.groovydoc.OutputTool" : this.outputToolClass);
322 Class<?> fileOutputToolClass = classWrangler.getClass(this.fileOutputToolClass == null ? "org.codehaus.groovy.tools.groovydoc.FileOutputTool" : this.fileOutputToolClass);
323 Class<?> resourceManagerClass = classWrangler.getClass(this.resourceManagerClass == null ? "org.codehaus.groovy.tools.groovydoc.ResourceManager" : this.resourceManagerClass);
324 Class<?> classpathResourceManagerClass = classWrangler.getClass(this.classpathResourceManagerClass == null ? "org.codehaus.groovy.tools.groovydoc.ClasspathResourceManager" : this.classpathResourceManagerClass);
325
326
327 if (attachGroovyDocAnnotation) {
328 if (groovyAtLeast(GROOVY_3_0_0_ALPHA_4)) {
329 System.setProperty("runtimeGroovydoc", "true");
330 } else {
331 getLog().warn("Requested to enable attaching GroovyDoc annotation, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_3_0_0_ALPHA_4 + " or newer). Ignoring enableGroovyDocAnnotation parameter.");
332 }
333 }
334 Properties docProperties = setupProperties();
335 Object fileOutputTool = invokeConstructor(findConstructor(fileOutputToolClass));
336 Object classpathResourceManager = invokeConstructor(findConstructor(classpathResourceManagerClass));
337 FileSetManager fileSetManager = new FileSetManager();
338 List<String> sourceDirectoriesStrings = new ArrayList<>();
339 for (FileSet sourceDirectory : sourceDirectories) {
340 sourceDirectoriesStrings.add(sourceDirectory.getDirectory());
341 }
342 GroovyDocTemplateInfo groovyDocTemplateInfo = new GroovyDocTemplateInfo(classWrangler.getGroovyVersion());
343 List<?> groovyDocLinks = setupLinks();
344 if (groovyOlderThan(GROOVY_1_6_0_RC2)) {
345 getLog().warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support GroovyDoc documentation properties (docTitle, footer, header, displayAuthor, overviewFile, and scope). You need Groovy 1.6-RC-2 or newer to support this. Ignoring properties.");
346 }
347
348
349 List<String> groovyDocSources = setupGroovyDocSources(sourceDirectories, fileSetManager);
350
351
352 Object groovyDocTool = createGroovyDocTool(groovyDocToolClass, resourceManagerClass, docProperties, classpathResourceManager, sourceDirectoriesStrings, groovyDocTemplateInfo, groovyDocLinks);
353
354
355 generateGroovyDoc(outputDirectory, groovyDocToolClass, outputToolClass, fileOutputTool, groovyDocSources, groovyDocTool);
356
357
358 if (stylesheetFile != null) {
359 copyStylesheet(outputDirectory);
360 }
361 }
362
363
364
365
366
367
368 protected Properties setupProperties() {
369 Properties properties = new Properties();
370 properties.setProperty("windowTitle", windowTitle);
371 properties.setProperty("docTitle", docTitle);
372 properties.setProperty("footer", footer);
373 properties.setProperty("header", header);
374 properties.setProperty("author", Boolean.toString(displayAuthor));
375 properties.setProperty("overviewFile", overviewFile != null ? overviewFile.getAbsolutePath() : "");
376 try {
377 Scopes scopeVal = Scopes.valueOf(scope.toUpperCase());
378 if (scopeVal.equals(Scopes.PUBLIC)) {
379 properties.setProperty("publicScope", "true");
380 } else if (scopeVal.equals(Scopes.PROTECTED)) {
381 properties.setProperty("protectedScope", "true");
382 } else if (scopeVal.equals(Scopes.PACKAGE)) {
383 properties.setProperty("packageScope", "true");
384 } else if (scopeVal.equals(Scopes.PRIVATE)) {
385 properties.setProperty("privateScope", "true");
386 }
387 } catch (IllegalArgumentException e) {
388 getLog().warn("Scope (" + scope + ") was not recognized. Skipping argument.");
389 }
390
391 return properties;
392 }
393
394
395
396
397
398
399
400
401
402
403 @SuppressWarnings({"rawtypes", "unchecked"})
404 protected List<?> setupLinks() throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
405 List linksList = new ArrayList();
406 if (links != null && !links.isEmpty()) {
407 Class<?> linkArgumentClass = null;
408 if (this.linkArgumentClass == null) {
409 if (groovyAtLeast(GROOVY_1_6_0_RC2)) {
410 linkArgumentClass = classWrangler.getClass("org.codehaus.groovy.tools.groovydoc.LinkArgument");
411 } else if (groovyAtLeast(GROOVY_1_5_2)) {
412 linkArgumentClass = classWrangler.getClass("org.codehaus.groovy.ant.Groovydoc$LinkArgument");
413 }
414 } else {
415 linkArgumentClass = classWrangler.getClass(this.linkArgumentClass);
416 }
417 if (linkArgumentClass != null) {
418 Method setHref = findMethod(linkArgumentClass, "setHref", String.class);
419 Method setPackages = findMethod(linkArgumentClass, "setPackages", String.class);
420 for (Link link : links) {
421 Object linkArgument = invokeConstructor(findConstructor(linkArgumentClass));
422 invokeMethod(setHref, linkArgument, link.getHref());
423 invokeMethod(setPackages, linkArgument, link.getPackages());
424 linksList.add(linkArgument);
425 }
426 } else {
427 getLog().warn("Requested to use GroovyDoc links, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be 1.5.2 or newer). Ignoring links parameter.");
428 }
429 }
430
431 return linksList;
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449 protected Object createGroovyDocTool(final Class<?> groovyDocToolClass, final Class<?> resourceManagerClass, final Properties docProperties, final Object classpathResourceManager, final List<String> sourceDirectories, final GroovyDocTemplateInfo groovyDocTemplateInfo, final List<?> groovyDocLinks) throws InvocationTargetException, IllegalAccessException, InstantiationException {
450 Object groovyDocTool;
451 if ((groovyAtLeast(GROOVY_4_0_27) && groovyOlderThan(GROOVY_5_0_0_ALPHA_1)) || groovyAtLeast(GROOVY_5_0_0_BETA_1)) {
452 groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String[].class, String[].class, String[].class, String[].class, List.class, String.class, Properties.class),
453 classpathResourceManager,
454 sourceDirectories.toArray(new String[0]),
455 defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
456 defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
457 defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
458 groovyDocLinks,
459 languageLevel,
460 docProperties
461 );
462 } else if (groovyAtLeast(GROOVY_1_6_0_RC2)) {
463 groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String[].class, String[].class, String[].class, String[].class, List.class, Properties.class),
464 classpathResourceManager,
465 sourceDirectories.toArray(new String[0]),
466 defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
467 defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
468 defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
469 groovyDocLinks,
470 docProperties
471 );
472 } else if (groovyAtLeast(GROOVY_1_5_2)) {
473 groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String.class, String[].class, String[].class, String[].class, List.class),
474 classpathResourceManager,
475 sourceDirectories.get(0),
476 defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
477 defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
478 defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
479 groovyDocLinks
480 );
481 if (sourceDirectories.size() > 1) {
482 getLog().warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support more than one GroovyDoc source directory (must be 1.6-RC-2 or newer). Only using first source directory (" + sourceDirectories.get(0) + ").");
483 }
484 } else {
485 groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String.class, String[].class, String[].class, String[].class),
486 classpathResourceManager,
487 sourceDirectories.get(0),
488 defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
489 defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
490 defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates
491 );
492 if (sourceDirectories.size() > 1) {
493 getLog().warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support more than one GroovyDoc source directory (must be 1.6-RC-2 or newer). Only using first source directory (" + sourceDirectories.get(0) + ").");
494 }
495 }
496
497 return groovyDocTool;
498 }
499
500
501
502
503
504
505
506
507 protected List<String> setupGroovyDocSources(final FileSet[] sourceDirectories, final FileSetManager fileSetManager) {
508 List<String> javaSources = new ArrayList<>();
509 List<String> groovySources = new ArrayList<>();
510 List<String> possibleGroovyStubs = new ArrayList<>();
511 for (FileSet sourceDirectory : sourceDirectories) {
512 String[] sources = fileSetManager.getIncludedFiles(sourceDirectory);
513 for (String source : sources) {
514 if (source.endsWith(".java") && !javaSources.contains(source)) {
515 javaSources.add(source);
516 } else if (!groovySources.contains(source)) {
517 groovySources.add(source);
518 possibleGroovyStubs.add(source.replaceFirst("\\." + FileUtils.getFileExtension(source), ".java"));
519 }
520 }
521 }
522 javaSources.removeAll(possibleGroovyStubs);
523 List<String> groovyDocSources = new ArrayList<>();
524 groovyDocSources.addAll(javaSources);
525 groovyDocSources.addAll(groovySources);
526
527 return groovyDocSources;
528 }
529
530
531
532
533
534
535
536
537
538
539
540
541
542 protected void generateGroovyDoc(final File outputDirectory, final Class<?> groovyDocToolClass, final Class<?> outputToolClass, final Object fileOutputTool, final List<String> groovyDocSources, final Object groovyDocTool) throws InvocationTargetException, IllegalAccessException {
543 getLog().debug("Adding sources to generate GroovyDoc for:");
544 if (getLog().isDebugEnabled()) {
545 for (String groovyDocSource : groovyDocSources) {
546 getLog().debug(" " + groovyDocSource);
547 }
548 }
549 if (groovyAtLeast(GROOVY_1_6_0_RC2)) {
550 invokeMethod(findMethod(groovyDocToolClass, "add", List.class), groovyDocTool, groovyDocSources);
551 } else {
552 Method add = findMethod(groovyDocToolClass, "add", String.class);
553 for (String groovyDocSource : groovyDocSources) {
554 invokeMethod(add, groovyDocTool, groovyDocSource);
555 }
556 }
557 invokeMethod(findMethod(groovyDocToolClass, "renderToOutput", outputToolClass, String.class), groovyDocTool, fileOutputTool, outputDirectory.getAbsolutePath());
558 }
559
560
561
562
563
564
565 protected void copyStylesheet(final File outputDirectory) {
566 getLog().info("Using stylesheet from " + stylesheetFile.getAbsolutePath() + ".");
567 try {
568 BufferedReader bufferedReader = null;
569 BufferedWriter bufferedWriter = null;
570 try {
571 if (stylesheetEncoding != null) {
572 bufferedReader = new BufferedReader(new InputStreamReader(Files.newInputStream(stylesheetFile.toPath()), stylesheetEncoding));
573 } else {
574 bufferedReader = new BufferedReader(new InputStreamReader(Files.newInputStream(stylesheetFile.toPath())));
575 }
576 StringBuilder css = new StringBuilder();
577 String line;
578 while ((line = bufferedReader.readLine()) != null) {
579 css.append(line).append("\n");
580 }
581 File outfile = new File(outputDirectory, "stylesheet.css");
582 if (stylesheetEncoding != null) {
583 bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(outfile.toPath()), stylesheetEncoding));
584 } else {
585 bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(outfile.toPath())));
586 }
587 bufferedWriter.write(css.toString());
588 } finally {
589 FileUtils.closeQuietly(bufferedReader);
590 FileUtils.closeQuietly(bufferedWriter);
591 }
592 } catch (IOException e) {
593 getLog().warn("Unable to copy specified stylesheet (" + stylesheetFile.getAbsolutePath() + ").");
594 }
595 }
596
597 }