View Javadoc
1   /*
2    * Copyright (C) 2011 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * The base GroovyDoc mojo, which all GroovyDoc mojos extend.
52   *
53   * @author Keegan Witt
54   * @since 1.0-beta-1
55   */
56  public abstract class AbstractGroovyDocMojo extends AbstractGroovySourcesMojo {
57  
58      /**
59       * Groovy 5.0.0-beta-1 version.
60       */
61      protected static final Version GROOVY_5_0_0_BETA_1 = new Version(5, 0, 0, "beta-1");
62  
63      /**
64       * Groovy 5.0.0-alpha-1 version.
65       */
66      protected static final Version GROOVY_5_0_0_ALPHA_1 = new Version(5, 0, 0, "alpha-1");
67  
68      /**
69       * Groovy 4.0.27 version.
70       */
71      protected static final Version GROOVY_4_0_27 = new Version(4, 0, 27);
72  
73      /**
74       * Groovy 3.0.0 alpha-4 version.
75       */
76      protected static final Version GROOVY_3_0_0_ALPHA_4 = new Version(3, 0, 0, "alpha-4");
77  
78      /**
79       * Groovy 1.6.0 RC-2 version.
80       */
81      protected static final Version GROOVY_1_6_0_RC2 = new Version(1, 6, 0, "RC-2");
82  
83      /**
84       * Groovy 1.6.0 RC-1 version.
85       */
86      protected static final Version GROOVY_1_6_0_RC1 = new Version(1, 6, 0, "RC-1");
87  
88      /**
89       * Groovy 1.5.8 version.
90       */
91      protected static final Version GROOVY_1_5_8 = new Version(1, 5, 8);
92  
93      /**
94       * Groovy 1.5.2 version.
95       */
96      protected static final Version GROOVY_1_5_2 = new Version(1, 5, 2);
97  
98      /**
99       * The window title.
100      */
101     @Parameter(defaultValue = "Groovy Documentation")
102     protected String windowTitle;
103 
104     /**
105      * The page title.
106      */
107     @Parameter(defaultValue = "Groovy Documentation")
108     protected String docTitle;
109 
110     /**
111      * The page footer.
112      */
113     @Parameter(defaultValue = "Groovy Documentation")
114     protected String footer;
115 
116     /**
117      * The Java language level to use for GroovyDoc generation.
118      */
119     @Parameter
120     protected LanguageLevel languageLevel;
121 
122     /**
123      * The page header.
124      */
125     @Parameter(defaultValue = "Groovy Documentation")
126     protected String header;
127 
128     /**
129      * Whether to display the author in the generated GroovyDoc.
130      */
131     @Parameter(defaultValue = "true")
132     protected boolean displayAuthor;
133 
134     /**
135      * The HTML file to be used for overview documentation.
136      */
137     @Parameter
138     protected File overviewFile;
139 
140     /**
141      * The stylesheet file (absolute path) to copy to output directory (will overwrite default stylesheet.css).
142      */
143     @Parameter
144     protected File stylesheetFile;
145 
146     /**
147      * The encoding of stylesheetFile.
148      */
149     @Parameter(defaultValue = "${project.build.sourceEncoding}")
150     protected String stylesheetEncoding;
151 
152     /**
153      * The scope to generate GroovyDoc for. Should be one of:
154      * <ul>
155      *   <li>"public"</li>
156      *   <li>"protected"</li>
157      *   <li>"package"</li>
158      *   <li>"private"</li>
159      * </ul>
160      */
161     @Parameter(defaultValue = "private")
162     protected String scope;
163 
164     /**
165      * Links to include in the generated GroovyDoc (key is link href, value is comma-separated packages to use that link).
166      *
167      * @since 1.0-beta-2
168      */
169     @Parameter
170     protected List<Link> links;
171 
172     /**
173      * Flag to allow GroovyDoc generation to be skipped.
174      *
175      * @since 1.6
176      */
177     @Parameter(property = "skipGroovydoc", defaultValue = "false")
178     protected boolean skipGroovyDoc;
179 
180     /**
181      * What classpath to include. One of
182      * <ul>
183      *   <li>PROJECT_ONLY</li>
184      *   <li>PROJECT_AND_PLUGIN</li>
185      *   <li>PLUGIN_ONLY</li>
186      * </ul>
187      * Uses the same scope as the required dependency resolution of this mojo. Use only if you know what you're doing.
188      *
189      * @since 1.8.0
190      */
191     @Parameter(defaultValue = "PROJECT_ONLY")
192     protected IncludeClasspath includeClasspath;
193 
194     /**
195      * Override the default Groovydoc default top-level templates. Uses Groovy's standard templates by default.
196      *
197      * @since 1.10.1
198      */
199     @Parameter
200     protected String[] defaultDocTemplates = null;
201 
202     /**
203      * Override the default Groovydoc package-level templates. Uses Groovy's standard templates by default.
204      *
205      * @since 1.10.1
206      */
207     @Parameter
208     protected String[] defaultPackageTemplates = null;
209 
210     /**
211      * Override the default Groovydoc class-level templates. Uses Groovy's standard templates by default.
212      *
213      * @since 1.10.1
214      */
215     @Parameter
216     protected String[] defaultClassTemplates = null;
217 
218     /**
219      * Allows you to override the class that is normally org.codehaus.groovy.tools.groovydoc.GroovyDocTool, for use when
220      * creating custom GroovyDoc implementations.
221      *
222      * @since 1.10.1
223      */
224     @Parameter
225     protected String groovyDocToolClass = null;
226 
227     /**
228      * Allows you to override the class that is normally org.codehaus.groovy.tools.groovydoc.OutputTool, for use when
229      * creating custom GroovyDoc implementations.
230      *
231      * @since 1.10.1
232      */
233     @Parameter
234     protected String outputToolClass = null;
235 
236     /**
237      * Allows you to override the class that is normally org.codehaus.groovy.tools.groovydoc.FileOutputTool, for use
238      * when creating custom GroovyDoc implementations.
239      *
240      * @since 1.10.1
241      */
242     @Parameter
243     protected String fileOutputToolClass = null;
244 
245     /**
246      * Allows you to override the class that is normally org.codehaus.groovy.tools.groovydoc.ResourceManager, for use
247      * when creating custom GroovyDoc implementations.
248      *
249      * @since 1.10.1
250      */
251     @Parameter
252     protected String resourceManagerClass = null;
253 
254     /**
255      * Allows you to override the class that is normally org.codehaus.groovy.tools.groovydoc.ClasspathResourceManager,
256      * for use when creating custom GroovyDoc implementations.
257      *
258      * @since 1.10.1
259      */
260     @Parameter
261     protected String classpathResourceManagerClass = null;
262 
263     /**
264      * Allows you to override the class that is normally org.codehaus.groovy.tools.groovydoc.LinkArgument (or
265      * org.codehaus.groovy.ant.Groovydoc$LinkArgument for Groovy older than 1.6-RC-2), for use when creating custom
266      * GroovyDoc implementations.
267      *
268      * @since 1.10.1
269      */
270     @Parameter
271     protected String linkArgumentClass = null;
272 
273     /**
274      * Enable attaching GroovyDoc annotation. Requires Groovy 3.0.0 alpha-4 or newer.
275      *
276      * @since 1.11.0
277      */
278     @Parameter(defaultValue = "false")
279     protected boolean attachGroovyDocAnnotation;
280 
281     /**
282      * Generates the GroovyDoc for the specified sources.
283      *
284      * @param sourceDirectories The source directories to generate GroovyDoc for
285      * @param classpath         The classpath to use for compilation
286      * @param outputDirectory   The directory to save the generated GroovyDoc in
287      * @throws ClassNotFoundException    when a class needed for GroovyDoc generation cannot be found
288      * @throws InstantiationException    when a class needed for GroovyDoc generation cannot be instantiated
289      * @throws IllegalAccessException    when a method needed for GroovyDoc generation cannot be accessed
290      * @throws InvocationTargetException when a reflection invocation needed for GroovyDoc generation cannot be completed
291      * @throws MalformedURLException     when a classpath element provides a malformed URL
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             // Groovy 1.5.8 and 1.6-RC-1 are blacklisted because of their dependency on org.apache.tools.ant.types.Path in GroovyDocTool constructor
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         // get classes we need with reflection
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         // set up GroovyDoc options
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         // prevent Java stubs (which lack Javadoc) from overwriting GroovyDoc by removing Java sources
349         List<String> groovyDocSources = setupGroovyDocSources(sourceDirectories, fileSetManager);
350 
351         // instantiate GroovyDocTool
352         Object groovyDocTool = createGroovyDocTool(groovyDocToolClass, resourceManagerClass, docProperties, classpathResourceManager, sourceDirectoriesStrings, groovyDocTemplateInfo, groovyDocLinks);
353 
354         // generate GroovyDoc
355         generateGroovyDoc(outputDirectory, groovyDocToolClass, outputToolClass, fileOutputTool, groovyDocSources, groovyDocTool);
356 
357         // overwrite stylesheet.css with provided stylesheet (if configured)
358         if (stylesheetFile != null) {
359             copyStylesheet(outputDirectory);
360         }
361     }
362 
363     /**
364      * Sets up the documentation properties.
365      *
366      * @return the documentation properties
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      * Sets up the GroovyDoc links.
396      *
397      * @return the GroovyDoc links
398      * @throws ClassNotFoundException    when a class needed for setting up GroovyDoc links cannot be found
399      * @throws InstantiationException    when a class needed for setting up GroovyDoc links cannot be instantiated
400      * @throws IllegalAccessException    when a method needed for setting up GroovyDoc links cannot be accessed
401      * @throws InvocationTargetException when a reflection invocation needed for setting up GroovyDoc links cannot be completed
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      * Instantiates a new GroovyDocTool.
436      *
437      * @param groovyDocToolClass       the GroovyDocTool class
438      * @param resourceManagerClass     the ResourceManager lass
439      * @param docProperties            the documentation properties
440      * @param classpathResourceManager the ClasspathResourceManager for the GroovyDocTool
441      * @param sourceDirectories        the source directories for the GroovyDocTool
442      * @param groovyDocTemplateInfo    the GroovyDocTemplateInfo for the GroovyDocTool
443      * @param groovyDocLinks           the GroovyDoc links
444      * @return the GroovyDocTool to use in GroovyDoc generation
445      * @throws InstantiationException    when a class needed for setting up GroovyDoc tool cannot be instantiated
446      * @throws IllegalAccessException    when a method needed for setting up GroovyDoc tool cannot be accessed
447      * @throws InvocationTargetException when a reflection invocation needed for setting up GroovyDoc tool cannot be completed
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      * Gets the Groovy sources without the Java sources (since the Java sources don't have Javadoc).
502      *
503      * @param sourceDirectories the source directories to get the Groovy sources from
504      * @param fileSetManager    the FileSetmanager to use to get the included files
505      * @return the groovy sources
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      * Performs the GroovyDoc generation.
532      *
533      * @param outputDirectory    the directory to output the GroovyDoc to
534      * @param groovyDocToolClass the GroovyDocTool class
535      * @param outputToolClass    the OutputTool class
536      * @param fileOutputTool     the FileOutputTool to use for GroovyDoc generation
537      * @param groovyDocSources   the sources to
538      * @param groovyDocTool      the GroovyDocTool to use for GroovyDoc generation
539      * @throws IllegalAccessException    when a method needed for GroovyDoc generation cannot be accessed
540      * @throws InvocationTargetException when a reflection invocation needed for GroovyDoc generation cannot be completed
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      * Copies the stylesheet to the specified output directory.
562      *
563      * @param outputDirectory The output directory to copy the stylesheet to
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 }