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