View Javadoc
1   package org.apache.maven.plugins.javadoc;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.lang3.ClassUtils;
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.ArtifactUtils;
25  import org.apache.maven.artifact.handler.ArtifactHandler;
26  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
29  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
30  import org.apache.maven.artifact.versioning.ArtifactVersion;
31  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
32  import org.apache.maven.execution.MavenSession;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.model.Resource;
36  import org.apache.maven.plugin.AbstractMojo;
37  import org.apache.maven.plugin.MojoExecution;
38  import org.apache.maven.plugin.MojoExecutionException;
39  import org.apache.maven.plugin.MojoFailureException;
40  import org.apache.maven.plugins.annotations.Component;
41  import org.apache.maven.plugins.annotations.Parameter;
42  import org.apache.maven.plugins.javadoc.options.BootclasspathArtifact;
43  import org.apache.maven.plugins.javadoc.options.DocletArtifact;
44  import org.apache.maven.plugins.javadoc.options.Group;
45  import org.apache.maven.plugins.javadoc.options.JavadocOptions;
46  import org.apache.maven.plugins.javadoc.options.JavadocPathArtifact;
47  import org.apache.maven.plugins.javadoc.options.OfflineLink;
48  import org.apache.maven.plugins.javadoc.options.ResourcesArtifact;
49  import org.apache.maven.plugins.javadoc.options.Tag;
50  import org.apache.maven.plugins.javadoc.options.Taglet;
51  import org.apache.maven.plugins.javadoc.options.TagletArtifact;
52  import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Writer;
53  import org.apache.maven.plugins.javadoc.resolver.JavadocBundle;
54  import org.apache.maven.plugins.javadoc.resolver.ResourceResolver;
55  import org.apache.maven.plugins.javadoc.resolver.SourceResolverConfig;
56  import org.apache.maven.project.MavenProject;
57  import org.apache.maven.project.ProjectBuilder;
58  import org.apache.maven.project.ProjectBuildingException;
59  import org.apache.maven.project.ProjectBuildingRequest;
60  import org.apache.maven.reporting.MavenReportException;
61  import org.apache.maven.settings.Proxy;
62  import org.apache.maven.settings.Settings;
63  import org.apache.maven.shared.artifact.DefaultArtifactCoordinate;
64  import org.apache.maven.shared.artifact.filter.resolve.AndFilter;
65  import org.apache.maven.shared.artifact.filter.resolve.PatternExclusionsFilter;
66  import org.apache.maven.shared.artifact.filter.resolve.PatternInclusionsFilter;
67  import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
68  import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
69  import org.apache.maven.shared.artifact.resolve.ArtifactResolver;
70  import org.apache.maven.shared.artifact.resolve.ArtifactResolverException;
71  import org.apache.maven.shared.artifact.resolve.ArtifactResult;
72  import org.apache.maven.shared.dependencies.DefaultDependableCoordinate;
73  import org.apache.maven.shared.dependencies.resolve.DependencyResolver;
74  import org.apache.maven.shared.dependencies.resolve.DependencyResolverException;
75  import org.apache.maven.shared.invoker.MavenInvocationException;
76  import org.apache.maven.toolchain.Toolchain;
77  import org.apache.maven.toolchain.ToolchainManager;
78  import org.apache.maven.wagon.PathUtils;
79  import org.codehaus.plexus.archiver.ArchiverException;
80  import org.codehaus.plexus.archiver.UnArchiver;
81  import org.codehaus.plexus.archiver.manager.ArchiverManager;
82  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
83  import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
84  import org.codehaus.plexus.languages.java.jpms.LocationManager;
85  import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
86  import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
87  import org.codehaus.plexus.languages.java.version.JavaVersion;
88  import org.codehaus.plexus.util.DirectoryScanner;
89  import org.codehaus.plexus.util.FileUtils;
90  import org.codehaus.plexus.util.IOUtil;
91  import org.codehaus.plexus.util.ReaderFactory;
92  import org.codehaus.plexus.util.StringUtils;
93  import org.codehaus.plexus.util.WriterFactory;
94  import org.codehaus.plexus.util.cli.CommandLineException;
95  import org.codehaus.plexus.util.cli.CommandLineUtils;
96  import org.codehaus.plexus.util.cli.Commandline;
97  import org.codehaus.plexus.util.xml.Xpp3Dom;
98  
99  import java.io.File;
100 import java.io.FileNotFoundException;
101 import java.io.FileOutputStream;
102 import java.io.IOException;
103 import java.io.InputStream;
104 import java.io.OutputStream;
105 import java.io.Writer;
106 import java.lang.reflect.InvocationTargetException;
107 import java.lang.reflect.Method;
108 import java.net.MalformedURLException;
109 import java.net.URI;
110 import java.net.URISyntaxException;
111 import java.net.URL;
112 import java.net.URLClassLoader;
113 import java.nio.file.Files;
114 import java.nio.file.Path;
115 import java.util.ArrayList;
116 import java.util.Arrays;
117 import java.util.Calendar;
118 import java.util.Collection;
119 import java.util.Collections;
120 import java.util.HashMap;
121 import java.util.HashSet;
122 import java.util.LinkedHashMap;
123 import java.util.LinkedHashSet;
124 import java.util.LinkedList;
125 import java.util.List;
126 import java.util.Locale;
127 import java.util.Map;
128 import java.util.Properties;
129 import java.util.Set;
130 import java.util.StringTokenizer;
131 import java.util.regex.Matcher;
132 import java.util.regex.Pattern;
133 
134 import static org.apache.maven.plugins.javadoc.JavadocUtil.toRelative;
135 import static org.apache.maven.plugins.javadoc.JavadocUtil.toList;
136 import static org.apache.maven.plugins.javadoc.JavadocUtil.isEmpty;
137 import static org.apache.maven.plugins.javadoc.JavadocUtil.isNotEmpty;
138 
139 /**
140  * Base class with majority of Javadoc functionalities.
141  *
142  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
143  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
144  * @version $Id: AbstractJavadocMojo.java 1801354 2017-07-09 08:49:46Z rfscholte $
145  * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html">
146  *      The Java API Documentation Generator, 7</a>
147  * @since 2.0
148  */
149 public abstract class AbstractJavadocMojo
150     extends AbstractMojo
151 {
152     /**
153      * Classifier used in the name of the javadoc-options XML file, and in the resources bundle
154      * artifact that gets attached to the project. This one is used for non-test javadocs.
155      *
156      * @see #TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER
157      * @since 2.7
158      */
159     public static final String JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "javadoc-resources";
160 
161     /**
162      * Classifier used in the name of the javadoc-options XML file, and in the resources bundle
163      * artifact that gets attached to the project. This one is used for test-javadocs.
164      *
165      * @see #JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER
166      * @since 2.7
167      */
168     public static final String TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "test-javadoc-resources";
169 
170     /**
171      * The default Javadoc API urls according the
172      * <a href="http://www.oracle.com/technetwork/java/javase/documentation/api-jsp-136079.html">Sun API
173      * Specifications</a>:
174      * <pre>
175      * &lt;javaApiLinks&gt;
176      *   &lt;property&gt;
177      *     &lt;name&gt;api_1.3&lt;/name&gt;
178      *     &lt;value&gt;http://docs.oracle.com/javase/1.3/docs/api/&lt;/value&gt;
179      *   &lt;/property&gt;
180      *   &lt;property&gt;
181      *     &lt;name&gt;api_1.4&lt;/name&gt;
182      *     &lt;value&gt;http://docs.oracle.com/javase/1.4.2/docs/api/&lt;/value&gt;
183      *   &lt;/property&gt;
184      *   &lt;property&gt;
185      *     &lt;name&gt;api_1.5&lt;/name&gt;
186      *     &lt;value&gt;http://docs.oracle.com/javase/1.5.0/docs/api/&lt;/value&gt;
187      *   &lt;/property&gt;
188      *   &lt;property&gt;
189      *     &lt;name&gt;api_1.6&lt;/name&gt;
190      *     &lt;value&gt;http://docs.oracle.com/javase/6/docs/api/&lt;/value&gt;
191      *   &lt;/property&gt;
192      *   &lt;property&gt;
193      *     &lt;name&gt;api_1.7&lt;/name&gt;
194      *     &lt;value&gt;http://docs.oracle.com/javase/7/docs/api/&lt;/value&gt;
195      *   &lt;/property&gt;
196      *   &lt;property&gt;
197      *     &lt;name&gt;api_1.8&lt;/name&gt;
198      *     &lt;value&gt;http://docs.oracle.com/javase/8/docs/api/&lt;/value&gt;
199      *   &lt;/property&gt;
200      * &lt;/javaApiLinks&gt;
201      * </pre>
202      *
203      * @since 2.6
204      */
205     public static final Properties DEFAULT_JAVA_API_LINKS = new Properties();
206 
207     /**
208      * The Javadoc script file name when <code>debug</code> parameter is on, i.e. javadoc.bat or javadoc.sh
209      */
210     protected static final String DEBUG_JAVADOC_SCRIPT_NAME = "javadoc." + ( SystemUtils.IS_OS_WINDOWS ? "bat" : "sh" );
211 
212     /**
213      * The <code>options</code> file name in the output directory when calling:
214      * <code>javadoc.exe(or .sh) &#x40;options &#x40;packages | &#x40;argfile | &#x40;files</code>
215      */
216     protected static final String OPTIONS_FILE_NAME = "options";
217 
218     /**
219      * The <code>packages</code> file name in the output directory when calling:
220      * <code>javadoc.exe(or .sh) &#x40;options &#x40;packages | &#x40;argfile | &#x40;files</code>
221      */
222     protected static final String PACKAGES_FILE_NAME = "packages";
223 
224     /**
225      * The <code>argfile</code> file name in the output directory when calling:
226      * <code>javadoc.exe(or .sh) &#x40;options &#x40;packages | &#x40;argfile | &#x40;files</code>
227      */
228     protected static final String ARGFILE_FILE_NAME = "argfile";
229 
230     /**
231      * The <code>files</code> file name in the output directory when calling:
232      * <code>javadoc.exe(or .sh) &#x40;options &#x40;packages | &#x40;argfile | &#x40;files</code>
233      */
234     protected static final String FILES_FILE_NAME = "files";
235 
236     /**
237      * The current class directory
238      */
239     private static final String RESOURCE_DIR = ClassUtils.getPackageName( JavadocReport.class ).replace( '.', '/' );
240 
241     /**
242      * Default css file name
243      */
244     private static final String DEFAULT_CSS_NAME = "stylesheet.css";
245 
246     /**
247      * Default location for css
248      */
249     private static final String RESOURCE_CSS_DIR = RESOURCE_DIR + "/css";
250 
251     /**
252      * For Javadoc options appears since Java 1.4.
253      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.1.html#summary">
254      * What's New in Javadoc 1.4</a>
255      *
256      * @since 2.1
257      */
258     private static final JavaVersion SINCE_JAVADOC_1_4 = JavaVersion.parse( "1.4" );
259 
260     /**
261      * For Javadoc options appears since Java 1.4.2.
262      * See <a
263      * href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
264      * What's New in Javadoc 1.4.2</a>
265      *
266      * @since 2.1
267      */
268     private static final JavaVersion SINCE_JAVADOC_1_4_2 = JavaVersion.parse( "1.4.2" );
269 
270     /**
271      * For Javadoc options appears since Java 5.0.
272      * See <a
273      * href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.5.0.html#commandlineoptions">
274      * What's New in Javadoc 5.0</a>
275      *
276      * @since 2.1
277      */
278     private static final JavaVersion SINCE_JAVADOC_1_5 = JavaVersion.parse( "1.5" );
279 
280     /**
281      * For Javadoc options appears since Java 6.0.
282      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/index.html">
283      * Javadoc Technology</a>
284      *
285      * @since 2.4
286      */
287     private static final JavaVersion SINCE_JAVADOC_1_6 = JavaVersion.parse( "1.6" );
288 
289     /**
290      * For Javadoc options appears since Java 8.0.
291      * See <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/index.html">
292      * Javadoc Technology</a>
293      *
294      * @since 3.0.0
295      */
296     private static final JavaVersion SINCE_JAVADOC_1_8 = JavaVersion.parse( "1.8" );
297 
298     /**
299      * 
300      */
301     private static final JavaVersion JAVA_VERSION = JavaVersion.JAVA_SPECIFICATION_VERSION;
302 
303     // ----------------------------------------------------------------------
304     // Mojo components
305     // ----------------------------------------------------------------------
306 
307     /**
308      * Archiver manager
309      *
310      * @since 2.5
311      */
312     @Component
313     private ArchiverManager archiverManager;
314 
315     @Component
316     private ResourceResolver resourceResolver;
317 
318     @Component
319     private ArtifactResolver artifactResolver;
320 
321     @Component
322     private ArtifactHandlerManager artifactHandlerManager;
323 
324     @Component
325     private DependencyResolver dependencyResolver;
326 
327     /**
328      * Project builder
329      *
330      * @since 3.0
331      */
332     @Component
333     private ProjectBuilder mavenProjectBuilder;
334 
335     /** */
336     @Component
337     private ToolchainManager toolchainManager;
338 
339     // ----------------------------------------------------------------------
340     // Mojo parameters
341     // ----------------------------------------------------------------------
342 
343     /**
344      * The current build session instance. This is used for
345      * toolchain manager API calls.
346      */
347     @Parameter( defaultValue = "${session}", readonly = true, required = true )
348     protected MavenSession session;
349 
350     /**
351      * The Maven Settings.
352      *
353      * @since 2.3
354      */
355     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
356     private Settings settings;
357 
358     /**
359      * The Maven Project Object
360      */
361     @Parameter( defaultValue = "${project}", readonly = true, required = true )
362     protected MavenProject project;
363 
364     @Parameter( defaultValue = "${mojoExecution}", readonly = true )
365     private MojoExecution mojo;
366 
367     /**
368      * Specify if the Javadoc should operate in offline mode.
369      */
370     @Parameter( defaultValue = "${settings.offline}", required = true, readonly = true )
371     private boolean isOffline;
372 
373     /**
374      * Specifies the Javadoc resources directory to be included in the Javadoc (i.e. package.html, images...).
375      * <br/>
376      * Could be used in addition of <code>docfilessubdirs</code> parameter.
377      * <br/>
378      * See <a href="#docfilessubdirs">docfilessubdirs</a>.
379      *
380      * @see #docfilessubdirs
381      * @since 2.1
382      */
383     @Parameter( defaultValue = "${basedir}/src/main/javadoc" )
384     private File javadocDirectory;
385 
386     /**
387      * Set an additional option(s) on the command line. This value should include quotes as necessary for
388      * parameters that include spaces. Useful for a custom doclet.
389      * 
390      * @since 3.0.0
391      */
392     @Parameter
393     private String[] additionalOptions;
394 
395     /**
396      * Set an additional Javadoc option(s) (i.e. JVM options) on the command line.
397      * Example:
398      * <pre>
399      * &lt;additionalJOption&gt;-J-Xss128m&lt;/additionalJOption&gt;
400      * </pre>
401      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#J">Jflag</a>.
402      * <br/>
403      * See <a href="http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp">vmoptions</a>.
404      * <br/>
405      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/net/properties.html">Networking
406      * Properties</a>.
407      *
408      * @since 2.3
409      */
410     @Parameter( property = "additionalJOption" )
411     private String additionalJOption;
412 
413     /**
414      * Set additional JVM options for the execution of the javadoc command via the '-J' option to javadoc.
415      * Example:
416      * <pre>
417      *     &lt;additionalJOptions&gt;
418      *         &lt;additionalJOption&gt;-J-Xmx1g &lt;/additionalJOption&gt;
419      *     &lt;/additionalJOptions&gt;
420      * </pre>
421      * @since 2.9
422      */
423     @Parameter
424     private String[] additionalJOptions;
425 
426     /**
427      * A list of artifacts containing resources which should be copied into the
428      * Javadoc output directory (like stylesheets, icons, etc.).
429      * <br/>
430      * Example:
431      * <pre>
432      * &lt;resourcesArtifacts&gt;
433      *   &lt;resourcesArtifact&gt;
434      *     &lt;groupId&gt;external.group.id&lt;/groupId&gt;
435      *     &lt;artifactId&gt;external-resources&lt;/artifactId&gt;
436      *     &lt;version&gt;1.0&lt;/version&gt;
437      *   &lt;/resourcesArtifact&gt;
438      * &lt;/resourcesArtifacts&gt;
439      * </pre>
440      * <br/>
441      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/ResourcesArtifact.html">Javadoc</a>.
442      * <br/>
443      *
444      * @since 2.5
445      */
446     @Parameter( property = "resourcesArtifacts" )
447     private ResourcesArtifact[] resourcesArtifacts;
448 
449     /**
450      * The local repository where the artifacts are located.
451      */
452     @Parameter( property = "localRepository" )
453     private ArtifactRepository localRepository;
454 
455     /**
456      * The projects in the reactor for aggregation report.
457      */
458     @Parameter( property = "reactorProjects", readonly = true )
459     private List<MavenProject> reactorProjects;
460 
461     /**
462      * Set this to <code>true</code> to debug the Javadoc plugin. With this, <code>javadoc.bat(or.sh)</code>,
463      * <code>options</code>, <code>@packages</code> or <code>argfile</code> files are provided in the output directory.
464      * <br/>
465      *
466      * @since 2.1
467      */
468     @Parameter( property = "debug", defaultValue = "false" )
469     private boolean debug;
470 
471     /**
472      * Sets the absolute path of the Javadoc Tool executable to use. Since version 2.5, a mere directory specification
473      * is sufficient to have the plugin use "javadoc" or "javadoc.exe" respectively from this directory.
474      *
475      * @since 2.3
476      */
477     @Parameter( property = "javadocExecutable" )
478     private String javadocExecutable;
479 
480     /**
481      * Version of the Javadoc Tool executable to use, ex. "1.3", "1.5".
482      *
483      * @since 2.3
484      */
485     @Parameter( property = "javadocVersion" )
486     private String javadocVersion;
487 
488     /**
489      * Version of the Javadoc Tool executable to use.
490      */
491     private JavaVersion javadocRuntimeVersion;
492 
493     /**
494      * Specifies whether the Javadoc generation should be skipped.
495      *
496      * @since 2.5
497      */
498     @Parameter( property = "maven.javadoc.skip", defaultValue = "false" )
499     protected boolean skip;
500 
501     /**
502      * Specifies if the build will fail if there are errors during javadoc execution or not.
503      *
504      * @since 2.5
505      */
506     @Parameter( property = "maven.javadoc.failOnError", defaultValue = "true" )
507     protected boolean failOnError;
508 
509 
510     /**
511      * Specifies if the build will fail if there are warning during javadoc execution or not.
512      *
513      * @since 3.0.1
514      */
515     @Parameter( property = "maven.javadoc.failOnWarnings", defaultValue = "false" )
516     protected boolean failOnWarnings;
517 
518     /**
519      * Specifies to use the
520      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#standard">
521      * options provided by the Standard Doclet</a> for a custom doclet.
522      * <br>
523      * Example:
524      * <pre>
525      * &lt;docletArtifacts&gt;
526      *   &lt;docletArtifact&gt;
527      *     &lt;groupId&gt;com.sun.tools.doclets&lt;/groupId&gt;
528      *     &lt;artifactId&gt;doccheck&lt;/artifactId&gt;
529      *     &lt;version&gt;1.2b2&lt;/version&gt;
530      *   &lt;/docletArtifact&gt;
531      * &lt;/docletArtifacts&gt;
532      * &lt;useStandardDocletOptions&gt;true&lt;/useStandardDocletOptions&gt;
533      * </pre>
534      *
535      * @since 2.5
536      */
537     @Parameter( property = "useStandardDocletOptions", defaultValue = "true" )
538     protected boolean useStandardDocletOptions;
539 
540     /**
541      * Detect the Javadoc links for all dependencies defined in the project. The detection is based on the default
542      * Maven conventions, i.e.: <code>${project.url}/apidocs</code>.
543      * <br/>
544      * For instance, if the project has a dependency to
545      * <a href="http://commons.apache.org/lang/">Apache Commons Lang</a> i.e.:
546      * <pre>
547      * &lt;dependency&gt;
548      *   &lt;groupId&gt;commons-lang&lt;/groupId&gt;
549      *   &lt;artifactId&gt;commons-lang&lt;/artifactId&gt;
550      * &lt;/dependency&gt;
551      * </pre>
552      * The added Javadoc <code>-link</code> parameter will be <code>http://commons.apache.org/lang/apidocs</code>.
553      *
554      * @see #links
555      * @since 2.6
556      */
557     @Parameter( property = "detectLinks", defaultValue = "false" )
558     private boolean detectLinks;
559 
560     /**
561      * Detect the links for all modules defined in the project.
562      * <br/>
563      * If {@link #reactorProjects} is defined in a non-aggregator way, it generates default offline links
564      * between modules based on the defined project's urls. For instance, if a parent project has two projects
565      * <code>module1</code> and <code>module2</code>, the <code>-linkoffline</code> will be:
566      * <br/>
567      * The added Javadoc <code>-linkoffline</code> parameter for <b>module1</b> will be
568      * <code>/absolute/path/to/</code><b>module2</b><code>/target/site/apidocs</code>
569      * <br/>
570      * The added Javadoc <code>-linkoffline</code> parameter for <b>module2</b> will be
571      * <code>/absolute/path/to/</code><b>module1</b><code>/target/site/apidocs</code>
572      *
573      * @see #offlineLinks
574      * @since 2.6
575      */
576     @Parameter( property = "detectOfflineLinks", defaultValue = "true" )
577     private boolean detectOfflineLinks;
578 
579     /**
580      * Detect the Java API link for the current build, i.e. <code>http://docs.oracle.com/javase/1.4.2/docs/api/</code>;
581      * for Java source 1.4.
582      * <br/>
583      * By default, the goal detects the Javadoc API link depending the value of the <code>source</code>
584      * parameter in the <code>org.apache.maven.plugins:maven-compiler-plugin</code>
585      * (defined in <code>${project.build.plugins}</code> or in <code>${project.build.pluginManagement}</code>),
586      * or try to compute it from the {@link #javadocExecutable} version.
587      * <br/>
588      * See
589      * <a href="./apidocs/org/apache/maven/plugin/javadoc/AbstractJavadocMojo.html#DEFAULT_JAVA_API_LINKS">Javadoc</a>
590      * for the default values.
591      * <br/>
592      *
593      * @see #links
594      * @see #javaApiLinks
595      * @see #DEFAULT_JAVA_API_LINKS
596      * @since 2.6
597      */
598     @Parameter( property = "detectJavaApiLink", defaultValue = "true" )
599     private boolean detectJavaApiLink;
600 
601     /**
602      * Use this parameter <b>only</b> if the <a href="http://java.sun.com/reference/api/index.html">Sun Javadoc API</a>
603      * urls have been changed or to use custom urls for Javadoc API url.
604      * <br/>
605      * See
606      * <a href="./apidocs/org/apache/maven/plugin/javadoc/AbstractJavadocMojo.html#DEFAULT_JAVA_API_LINKS">Javadoc</a>
607      * for the default values.
608      * <br/>
609      *
610      * @see #DEFAULT_JAVA_API_LINKS
611      * @since 2.6
612      */
613     @Parameter( property = "javaApiLinks" )
614     private Properties javaApiLinks;
615 
616     /**
617      * Flag controlling content validation of <code>package-list</code> resources. If set, the content of
618      * <code>package-list</code> resources will be validated.
619      *
620      * @since 2.8
621      */
622     @Parameter( property = "validateLinks", defaultValue = "false" )
623     private boolean validateLinks;
624 
625     // ----------------------------------------------------------------------
626     // Javadoc Options - all alphabetical
627     // ----------------------------------------------------------------------
628 
629     /**
630      * Specifies the paths where the boot classes reside. The <code>bootclasspath</code> can contain multiple paths
631      * by separating them with a colon (<code>:</code>) or a semi-colon (<code>;</code>).
632      * <br/>
633      * See <a
634      * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#bootclasspath">bootclasspath</a>.
635      * <br/>
636      *
637      * @since 2.5
638      */
639     @Parameter( property = "bootclasspath" )
640     private String bootclasspath;
641 
642     /**
643      * Specifies the artifacts where the boot classes reside.
644      * <br/>
645      * See <a
646      * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#bootclasspath">bootclasspath</a>.
647      * <br/>
648      * Example:
649      * <pre>
650      * &lt;bootclasspathArtifacts&gt;
651      *   &lt;bootclasspathArtifact&gt;
652      *     &lt;groupId&gt;my-groupId&lt;/groupId&gt;
653      *     &lt;artifactId&gt;my-artifactId&lt;/artifactId&gt;
654      *     &lt;version&gt;my-version&lt;/version&gt;
655      *   &lt;/bootclasspathArtifact&gt;
656      * &lt;/bootclasspathArtifacts&gt;
657      * </pre>
658      * <br/>
659      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/BootclasspathArtifact.html">Javadoc</a>.
660      * <br/>
661      *
662      * @since 2.5
663      */
664     @Parameter( property = "bootclasspathArtifacts" )
665     private BootclasspathArtifact[] bootclasspathArtifacts;
666 
667     /**
668      * Uses the sentence break iterator to determine the end of the first sentence.
669      * <br/>
670      * See <a
671      * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#breakiterator">breakiterator</a>.
672      * <br/>
673      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
674      * 1.4</a>.
675      * <br/>
676      */
677     @Parameter( property = "breakiterator", defaultValue = "false" )
678     private boolean breakiterator;
679 
680     /**
681      * Specifies the class file that starts the doclet used in generating the documentation.
682      * <br/>
683      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#doclet">doclet</a>.
684      */
685     @Parameter( property = "doclet" )
686     private String doclet;
687 
688     /**
689      * Specifies the artifact containing the doclet starting class file (specified with the <code>-doclet</code>
690      * option).
691      * <br/>
692      * See
693      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docletpath">docletpath</a>.
694      * <br/>
695      * Example:
696      * <pre>
697      * &lt;docletArtifact&gt;
698      *   &lt;groupId&gt;com.sun.tools.doclets&lt;/groupId&gt;
699      *   &lt;artifactId&gt;doccheck&lt;/artifactId&gt;
700      *   &lt;version&gt;1.2b2&lt;/version&gt;
701      * &lt;/docletArtifact&gt;
702      * </pre>
703      * <br/>
704      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/DocletArtifact.html">Javadoc</a>.
705      * <br/>
706      */
707     @Parameter( property = "docletArtifact" )
708     private DocletArtifact docletArtifact;
709 
710     /**
711      * Specifies multiple artifacts containing the path for the doclet starting class file (specified with the
712      * <code>-doclet</code> option).
713      * <br/>
714      * See
715      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docletpath">docletpath</a>.
716      * <br/>
717      * Example:
718      * <pre>
719      * &lt;docletArtifacts&gt;
720      *   &lt;docletArtifact&gt;
721      *     &lt;groupId&gt;com.sun.tools.doclets&lt;/groupId&gt;
722      *     &lt;artifactId&gt;doccheck&lt;/artifactId&gt;
723      *     &lt;version&gt;1.2b2&lt;/version&gt;
724      *   &lt;/docletArtifact&gt;
725      * &lt;/docletArtifacts&gt;
726      * </pre>
727      * <br/>
728      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/DocletArtifact.html">Javadoc</a>.
729      * <br/>
730      *
731      * @since 2.1
732      */
733     @Parameter( property = "docletArtifacts" )
734     private DocletArtifact[] docletArtifacts;
735 
736     /**
737      * Specifies the path to the doclet starting class file (specified with the <code>-doclet</code> option) and
738      * any jar files it depends on. The <code>docletPath</code> can contain multiple paths by separating them with
739      * a colon (<code>:</code>) or a semi-colon (<code>;</code>).
740      * <br/>
741      * See
742      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docletpath">docletpath</a>.
743      */
744     @Parameter( property = "docletPath" )
745     private String docletPath;
746 
747     /**
748      * Specifies the encoding name of the source files. If not specificed, the encoding value will be the value of the
749      * <code>file.encoding</code> system property.
750      * <br/>
751      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#encoding">encoding</a>.
752      * <br/>
753      * <b>Note</b>: In 2.4, the default value was locked to <code>ISO-8859-1</code> to ensure reproducing build, but
754      * this was reverted in 2.5.
755      * <br/>
756      */
757     @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
758     private String encoding;
759 
760     /**
761      * Unconditionally excludes the specified packages and their subpackages from the list formed by
762      * <code>-subpackages</code>. Multiple packages can be separated by commas (<code>,</code>), colons (<code>:</code>)
763      * or semicolons (<code>;</code>).
764      * <br/>
765      * Example:
766      * <pre>
767      * &lt;excludePackageNames&gt;*.internal:org.acme.exclude1.*:org.acme.exclude2&lt;/excludePackageNames&gt;
768      * </pre>
769      * <br/>
770      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#exclude">exclude</a>.
771      * <br/>
772      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
773      * 1.4</a>.
774      */
775     @Parameter( property = "excludePackageNames" )
776     private String excludePackageNames;
777 
778     /**
779      * Specifies the directories where extension classes reside. Separate directories in <code>extdirs</code> with a
780      * colon (<code>:</code>) or a semi-colon (<code>;</code>).
781      * <br/>
782      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#extdirs">extdirs</a>.
783      */
784     @Parameter( property = "extdirs" )
785     private String extdirs;
786 
787     /**
788      * Specifies the locale that javadoc uses when generating documentation.
789      * <br/>
790      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#locale">locale</a>.
791      */
792     @Parameter( property = "locale" )
793     private String locale;
794 
795     /**
796      * Specifies the maximum Java heap size to be used when launching the Javadoc tool.
797      * JVMs refer to this property as the <code>-Xmx</code> parameter. Example: '512' or '512m'.
798      * The memory unit depends on the JVM used. The units supported could be: <code>k</code>, <code>kb</code>,
799      * <code>m</code>, <code>mb</code>, <code>g</code>, <code>gb</code>, <code>t</code>, <code>tb</code>.
800      * If no unit specified, the default unit is <code>m</code>.
801      */
802     @Parameter( property = "maxmemory" )
803     private String maxmemory;
804 
805     /**
806      * Specifies the minimum Java heap size to be used when launching the Javadoc tool.
807      * JVMs refer to this property as the <code>-Xms</code> parameter. Example: '512' or '512m'.
808      * The memory unit depends on the JVM used. The units supported could be: <code>k</code>, <code>kb</code>,
809      * <code>m</code>, <code>mb</code>, <code>g</code>, <code>gb</code>, <code>t</code>, <code>tb</code>.
810      * If no unit specified, the default unit is <code>m</code>.
811      */
812     @Parameter( property = "minmemory" )
813     private String minmemory;
814 
815     /**
816      * This option creates documentation with the appearance and functionality of documentation generated by
817      * Javadoc 1.1.
818      * <br/>
819      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#1.1">1.1</a>.
820      * <br/>
821      */
822     @Parameter( property = "old", defaultValue = "false" )
823     private boolean old;
824 
825     /**
826      * Specifies that javadoc should retrieve the text for the overview documentation from the "source" file
827      * specified by path/filename and place it on the Overview page (overview-summary.html).
828      * <br/>
829      * <b>Note</b>: could be in conflict with &lt;nooverview/&gt;.
830      * <br/>
831      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#overview">overview</a>.
832      * <br/>
833      */
834     @Parameter( property = "overview", defaultValue = "${basedir}/src/main/javadoc/overview.html" )
835     private File overview;
836 
837     /**
838      * Shuts off non-error and non-warning messages, leaving only the warnings and errors appear, making them
839      * easier to view.
840      * <br/>
841      * Note: was a standard doclet in Java 1.4.2 (refer to bug ID
842      * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4714350">4714350</a>).
843      * <br/>
844      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#quiet">quiet</a>.
845      * <br/>
846      * Since Java 5.0.
847      * <br/>
848      */
849     @Parameter( property = "quiet", defaultValue = "false" )
850     private boolean quiet;
851 
852     /**
853      * Specifies the access level for classes and members to show in the Javadocs.
854      * Possible values are:
855      * <ul>
856      * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#public">public</a>
857      * (shows only public classes and members)</li>
858      * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#protected">protected</a>
859      * (shows only public and protected classes and members)</li>
860      * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#package">package</a>
861      * (shows all classes and members not marked private)</li>
862      * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#private">private</a>
863      * (shows all classes and members)</li>
864      * </ul>
865      * <br/>
866      */
867     @Parameter( property = "show", defaultValue = "protected" )
868     private String show;
869 
870     /**
871      * Necessary to enable javadoc to handle assertions introduced in J2SE v 1.4 source code or generics introduced in
872      * J2SE v5.
873      * <br/>
874      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#source">source</a>.
875      * <br/>
876      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
877      * 1.4</a>.
878      */
879     @Parameter( property = "source" )
880     private String source;
881 
882     /**
883      * Specifies the source paths where the subpackages are located. The <code>sourcepath</code> can contain
884      * multiple paths by separating them with a colon (<code>:</code>) or a semi-colon (<code>;</code>).
885      * <br/>
886      * See
887      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#sourcepath">sourcepath</a>.
888      */
889     @Parameter( property = "sourcepath" )
890     private String sourcepath;
891 
892     /**
893      * Specifies the package directory where javadoc will be executed. Multiple packages can be separated by
894      * colons (<code>:</code>).
895      * <br/>
896      * See
897      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#subpackages">subpackages</a>.
898      * <br/>
899      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
900      * 1.4</a>.
901      */
902     @Parameter( property = "subpackages" )
903     private String subpackages;
904 
905     /**
906      * Provides more detailed messages while javadoc is running.
907      * <br/>
908      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#verbose">verbose</a>.
909      * <br/>
910      */
911     @Parameter( property = "verbose", defaultValue = "false" )
912     private boolean verbose;
913 
914     // ----------------------------------------------------------------------
915     // Standard Doclet Options - all alphabetical
916     // ----------------------------------------------------------------------
917 
918     /**
919      * Specifies whether or not the author text is included in the generated Javadocs.
920      * <br/>
921      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#author">author</a>.
922      * <br/>
923      */
924     @Parameter( property = "author", defaultValue = "true" )
925     private boolean author;
926 
927     /**
928      * Specifies the text to be placed at the bottom of each output file.<br/>
929      * If you want to use html you have to put it in a CDATA section, <br/>
930      * eg. <code>&lt;![CDATA[Copyright 2005, &lt;a href="http://www.mycompany.com">MyCompany, Inc.&lt;a>]]&gt;</code>
931      * <br/>
932      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#bottom">bottom</a>.
933      * <br/>
934      */
935     @Parameter( property = "bottom",
936                     defaultValue = "Copyright &#169; {inceptionYear}&#x2013;{currentYear} {organizationName}. "
937                         + "All rights reserved." )
938     private String bottom;
939 
940     /**
941      * Specifies the HTML character set for this document. If not specificed, the charset value will be the value of
942      * the <code>docencoding</code> parameter.
943      * <br/>
944      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#charset">charset</a>.
945      * <br/>
946      */
947     @Parameter( property = "charset" )
948     private String charset;
949 
950     /**
951      * Specifies the encoding of the generated HTML files. If not specificed, the docencoding value will be
952      * <code>UTF-8</code>.
953      * <br/>
954      * See
955      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docencoding">docencoding</a>.
956      */
957     @Parameter( property = "docencoding", defaultValue = "${project.reporting.outputEncoding}" )
958     private String docencoding;
959 
960     /**
961      * Enables deep copying of the <code>&#42;&#42;/doc-files</code> directories and the specifc <code>resources</code>
962      * directory from the <code>javadocDirectory</code> directory (for instance,
963      * <code>src/main/javadoc/com/mycompany/myapp/doc-files</code> and <code>src/main/javadoc/resources</code>).
964      * <br/>
965      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docfilessubdirs">
966      * docfilessubdirs</a>.
967      * <br/>
968      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
969      * 1.4</a>.
970      * <br/>
971      * See <a href="#javadocDirectory">javadocDirectory</a>.
972      * <br/>
973      *
974      * @see #excludedocfilessubdir
975      * @see #javadocDirectory
976      */
977     @Parameter( property = "docfilessubdirs", defaultValue = "false" )
978     private boolean docfilessubdirs;
979 
980     /**
981      * Specifies specific checks to be performed on Javadoc comments.
982      * <br/>
983      * See <a href="http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#BEJEFABE">doclint</a>.
984      *
985      * @since 3.0.0
986      */
987     @Parameter( property = "doclint" )
988     private String doclint;
989 
990     /**
991      * Specifies the title to be placed near the top of the overview summary file.
992      * <br/>
993      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#doctitle">doctitle</a>.
994      * <br/>
995      */
996     @Parameter( property = "doctitle", defaultValue = "${project.name} ${project.version} API" )
997     private String doctitle;
998 
999     /**
1000      * Excludes any "doc-files" subdirectories with the given names. Multiple patterns can be excluded
1001      * by separating them with colons (<code>:</code>).
1002      * <br/>
1003      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#excludedocfilessubdir">
1004      * excludedocfilessubdir</a>.
1005      * <br/>
1006      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
1007      * 1.4</a>.
1008      *
1009      * @see #docfilessubdirs
1010      */
1011     @Parameter( property = "excludedocfilessubdir" )
1012     private String excludedocfilessubdir;
1013 
1014     /**
1015      * Specifies the footer text to be placed at the bottom of each output file.
1016      * <br/>
1017      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#footer">footer</a>.
1018      */
1019     @Parameter( property = "footer" )
1020     private String footer;
1021 
1022     /**
1023      * Separates packages on the overview page into whatever groups you specify, one group per table. The
1024      * packages pattern can be any package name, or can be the start of any package name followed by an asterisk
1025      * (<code>*</code>) meaning "match any characters". Multiple patterns can be included in a group
1026      * by separating them with colons (<code>:</code>).
1027      * <br/>
1028      * Example:
1029      * <pre>
1030      * &lt;groups&gt;
1031      *   &lt;group&gt;
1032      *     &lt;title&gt;Core Packages&lt;/title&gt;
1033      *     &lt;!-- To includes java.lang, java.lang.ref,
1034      *     java.lang.reflect and only java.util
1035      *     (i.e. not java.util.jar) --&gt;
1036      *     &lt;packages&gt;java.lang*:java.util&lt;/packages&gt;
1037      *   &lt;/group&gt;
1038      *   &lt;group&gt;
1039      *     &lt;title&gt;Extension Packages&lt;/title&gt;
1040      *     &nbsp;&lt;!-- To include javax.accessibility,
1041      *     javax.crypto, ... (among others) --&gt;
1042      *     &lt;packages&gt;javax.*&lt;/packages&gt;
1043      *   &lt;/group&gt;
1044      * &lt;/groups&gt;
1045      * </pre>
1046      * <b>Note</b>: using <code>java.lang.*</code> for <code>packages</code> would omit the <code>java.lang</code>
1047      * package but using <code>java.lang*</code> will include it.
1048      * <br/>
1049      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#group">group</a>.
1050      * <br/>
1051      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/Group.html">Javadoc</a>.
1052      * <br/>
1053      */
1054     @Parameter( property = "groups" )
1055     private Group[] groups;
1056 
1057     /**
1058      * Specifies the header text to be placed at the top of each output file.
1059      * <br/>
1060      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#header">header</a>.
1061      */
1062     @Parameter( property = "header" )
1063     private String header;
1064 
1065     /**
1066      * Specifies the path of an alternate help file path\filename that the HELP link in the top and bottom
1067      * navigation bars link to.
1068      * <br/>
1069      * <b>Note</b>: could be in conflict with &lt;nohelp/&gt;.
1070      * <br/>
1071      * The <code>helpfile</code> could be an absolute File path.
1072      * <br/>
1073      * Since 2.6, it could be also be a path from a resource in the current project source directories
1074      * (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
1075      * or from a resource in the Javadoc plugin dependencies, for instance:
1076      * <pre>
1077      * &lt;helpfile&gt;path/to/your/resource/yourhelp-doc.html&lt;/helpfile&gt;
1078      * </pre>
1079      * Where <code>path/to/your/resource/yourhelp-doc.html</code> could be in <code>src/main/javadoc</code>.
1080      * <pre>
1081      * &lt;build&gt;
1082      *   &lt;plugins&gt;
1083      *     &lt;plugin&gt;
1084      *       &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
1085      *       &lt;artifactId&gt;maven-javadoc-plugin&lt;/artifactId&gt;
1086      *       &lt;configuration&gt;
1087      *         &lt;helpfile&gt;path/to/your/resource/yourhelp-doc.html&lt;/helpfile&gt;
1088      *         ...
1089      *       &lt;/configuration&gt;
1090      *       &lt;dependencies&gt;
1091      *         &lt;dependency&gt;
1092      *           &lt;groupId&gt;groupId&lt;/groupId&gt;
1093      *           &lt;artifactId&gt;artifactId&lt;/artifactId&gt;
1094      *           &lt;version&gt;version&lt;/version&gt;
1095      *         &lt;/dependency&gt;
1096      *       &lt;/dependencies&gt;
1097      *     &lt;/plugin&gt;
1098      *     ...
1099      *   &lt;plugins&gt;
1100      * &lt;/build&gt;
1101      * </pre>
1102      * Where <code>path/to/your/resource/yourhelp-doc.html</code> is defined in the
1103      * <code>groupId:artifactId:version</code> javadoc plugin dependency.
1104      * <br/>
1105      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#helpfile">helpfile</a>.
1106      */
1107     @Parameter( property = "helpfile" )
1108     private String helpfile;
1109 
1110     /**
1111      * Adds HTML meta keyword tags to the generated file for each class.
1112      * <br/>
1113      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#keywords">keywords</a>.
1114      * <br/>
1115      * Since
1116      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
1117      * Java 1.4.2</a>.
1118      * <br/>
1119      * Since
1120      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.5.0.html#commandlineoptions">
1121      * Java 5.0</a>.
1122      * <br/>
1123      *
1124      * @since 2.1
1125      */
1126     @Parameter( property = "keywords", defaultValue = "false" )
1127     private boolean keywords;
1128 
1129     /**
1130      * Creates links to existing javadoc-generated documentation of external referenced classes.
1131      * <br>
1132      * <b>Notes</b>:
1133      * <ol>
1134      * <li>only used if {@link #isOffline} is set to <code>false</code>.</li>
1135      * <li>all given links should have a fetchable <code>/package-list</code> file. For instance:
1136      * <pre>
1137      * &lt;links&gt;
1138      *   &lt;link&gt;http://docs.oracle.com/javase/1.4.2/docs/api&lt;/link&gt;
1139      * &lt;links&gt;
1140      * </pre>
1141      * will be used because <code>http://docs.oracle.com/javase/1.4.2/docs/api/package-list</code> exists.</li>
1142      * <li>if {@link #detectLinks} is defined, the links between the project dependencies are
1143      * automatically added.</li>
1144      * <li>if {@link #detectJavaApiLink} is defined, a Java API link, based on the Java version of the
1145      * project's sources, will be added automatically.</li>
1146      * </ol>
1147      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#link">link</a>.
1148      *
1149      * @see #detectLinks
1150      * @see #detectJavaApiLink
1151      */
1152     @Parameter( property = "links" )
1153     protected ArrayList<String> links;
1154 
1155     /**
1156      * Creates an HTML version of each source file (with line numbers) and adds links to them from the standard
1157      * HTML documentation.
1158      * <br/>
1159      * See
1160      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#linksource">linksource</a>.
1161      * <br/>
1162      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
1163      * 1.4</a>.
1164      * <br/>
1165      */
1166     @Parameter( property = "linksource", defaultValue = "false" )
1167     private boolean linksource;
1168 
1169     /**
1170      * Suppress the entire comment body, including the main description and all tags, generating only declarations.
1171      * <br/>
1172      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nocomment">nocomment</a>.
1173      * <br/>
1174      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
1175      * 1.4</a>.
1176      * <br/>
1177      */
1178     @Parameter( property = "nocomment", defaultValue = "false" )
1179     private boolean nocomment;
1180 
1181     /**
1182      * Prevents the generation of any deprecated API at all in the documentation.
1183      * <br/>
1184      * See
1185      * <a
1186      * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nodeprecated">nodeprecated</a>.
1187      * <br/>
1188      */
1189     @Parameter( property = "nodeprecated", defaultValue = "false" )
1190     private boolean nodeprecated;
1191 
1192     /**
1193      * Prevents the generation of the file containing the list of deprecated APIs (deprecated-list.html) and the
1194      * link in the navigation bar to that page.
1195      * <br/>
1196      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nodeprecatedlist">
1197      * nodeprecatedlist</a>.
1198      * <br/>
1199      */
1200     @Parameter( property = "nodeprecatedlist", defaultValue = "false" )
1201     private boolean nodeprecatedlist;
1202 
1203     /**
1204      * Omits the HELP link in the navigation bars at the top and bottom of each page of output.
1205      * <br/>
1206      * <b>Note</b>: could be in conflict with &lt;helpfile/&gt;.
1207      * <br/>
1208      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nohelp">nohelp</a>.
1209      * <br/>
1210      */
1211     @Parameter( property = "nohelp", defaultValue = "false" )
1212     private boolean nohelp;
1213 
1214     /**
1215      * Omits the index from the generated docs.
1216      * <br/>
1217      * <b>Note</b>: could be in conflict with &lt;splitindex/&gt;.
1218      * <br/>
1219      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#noindex">noindex</a>.
1220      * <br/>
1221      */
1222     @Parameter( property = "noindex", defaultValue = "false" )
1223     private boolean noindex;
1224 
1225     /**
1226      * Omits the navigation bar from the generated docs.
1227      * <br/>
1228      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nonavbar">nonavbar</a>.
1229      * <br/>
1230      */
1231     @Parameter( property = "nonavbar", defaultValue = "false" )
1232     private boolean nonavbar;
1233 
1234     /**
1235      * Omits the entire overview page from the generated docs.
1236      * <br/>
1237      * <b>Note</b>: could be in conflict with &lt;overview/&gt;.
1238      * <br/>
1239      * Standard Doclet undocumented option.
1240      * <br/>
1241      *
1242      * @since 2.4
1243      */
1244     @Parameter( property = "nooverview", defaultValue = "false" )
1245     private boolean nooverview;
1246 
1247     /**
1248      * Omits qualifying package name from ahead of class names in output.
1249      * Example:
1250      * <pre>
1251      * &lt;noqualifier&gt;all&lt;/noqualifier&gt;
1252      * or
1253      * &lt;noqualifier&gt;packagename1:packagename2&lt;/noqualifier&gt;
1254      * </pre>
1255      * See
1256      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#noqualifier">noqualifier</a>.
1257      * <br/>
1258      * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
1259      * 1.4</a>.
1260      */
1261     @Parameter( property = "noqualifier" )
1262     private String noqualifier;
1263 
1264     /**
1265      * Omits from the generated docs the "Since" sections associated with the since tags.
1266      * <br/>
1267      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nosince">nosince</a>.
1268      * <br/>
1269      */
1270     @Parameter( property = "nosince", defaultValue = "false" )
1271     private boolean nosince;
1272 
1273     /**
1274      * Suppresses the timestamp, which is hidden in an HTML comment in the generated HTML near the top of each page.
1275      * <br/>
1276      * See
1277      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#notimestamp">notimestamp</a>.
1278      * <br/>
1279      * Since
1280      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.5.0.html#commandlineoptions">
1281      * Java 5.0</a>.
1282      * <br/>
1283      *
1284      * @since 2.1
1285      */
1286     @Parameter( property = "notimestamp", defaultValue = "false" )
1287     private boolean notimestamp;
1288 
1289     /**
1290      * Omits the class/interface hierarchy pages from the generated docs.
1291      * <br>
1292      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#notree">notree</a> option
1293      */
1294     @Parameter( property = "notree", defaultValue = "false" )
1295     private boolean notree;
1296 
1297     /**
1298      * This option is a variation of <code>-link</code>; they both create links to javadoc-generated documentation
1299      * for external referenced classes.
1300      * <br/>
1301      * See
1302      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#linkoffline">linkoffline</a>.
1303      * <br/>
1304      * Example:
1305      * <pre>
1306      * &lt;offlineLinks&gt;
1307      *   &lt;offlineLink&gt;
1308      *     &lt;url&gt;http://docs.oracle.com/javase/1.5.0/docs/api/&lt;/url&gt;
1309      *     &lt;location&gt;../javadoc/jdk-5.0/&lt;/location&gt;
1310      *   &lt;/offlineLink&gt;
1311      * &lt;/offlineLinks&gt;
1312      * </pre>
1313      * <br/>
1314      * <b>Note</b>: if {@link #detectOfflineLinks} is defined, the offline links between the project modules are
1315      * automatically added if the goal is calling in a non-aggregator way.
1316      * <br>
1317      * @see <a href="./apidocs/org/apache/maven/plugin/javadoc/options/OfflineLink.html">Javadoc</a>.
1318      */
1319     @Parameter( property = "offlineLinks" )
1320     private OfflineLink[] offlineLinks;
1321 
1322     /**
1323      * Specifies the destination directory where javadoc saves the generated HTML files.
1324      * <br>
1325      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#d">d</a> option
1326      */
1327     @Parameter( property = "destDir", alias = "destDir", defaultValue = "${project.build.directory}/apidocs",
1328                     required = true )
1329     protected File outputDirectory;
1330 
1331     /**
1332      * Specify the text for upper left frame.
1333      * <br/>
1334      * Since
1335      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
1336      * Java 1.4.2</a>.
1337      *
1338      * @since 2.1
1339      */
1340     @Parameter( property = "packagesheader" )
1341     private String packagesheader;
1342 
1343     /**
1344      * Generates compile-time warnings for missing serial tags.
1345      * <br/>
1346      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#serialwarn">serialwarn</a> option 
1347      */
1348     @Parameter( property = "serialwarn", defaultValue = "false" )
1349     private boolean serialwarn;
1350 
1351     /**
1352      * Specify the number of spaces each tab takes up in the source. If no tab is used in source, the default
1353      * space is used.
1354      * <br/>
1355      * Note: was <code>linksourcetab</code> in Java 1.4.2 (refer to bug ID
1356      * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4788919">4788919</a>).
1357      * <br/>
1358      * Since
1359      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
1360      * 1.4.2</a>.
1361      * <br/>
1362      * Since Java 5.0.
1363      *
1364      * @since 2.1
1365      */
1366     @Parameter( property = "sourcetab", alias = "linksourcetab" )
1367     private int sourcetab;
1368 
1369     /**
1370      * Splits the index file into multiple files, alphabetically, one file per letter, plus a file for any index
1371      * entries that start with non-alphabetical characters.
1372      * <br/>
1373      * <b>Note</b>: could be in conflict with &lt;noindex/&gt;.
1374      * <br/>
1375      * See
1376      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#splitindex">splitindex</a>.
1377      * <br/>
1378      */
1379     @Parameter( property = "splitindex", defaultValue = "false" )
1380     private boolean splitindex;
1381 
1382     /**
1383      * Specifies whether the stylesheet to be used is the <code>maven</code>'s javadoc stylesheet or
1384      * <code>java</code>'s default stylesheet when a <i>stylesheetfile</i> parameter is not specified.
1385      * <br/>
1386      * Possible values: <code>maven<code> or <code>java</code>.
1387      * <br/>
1388      */
1389     @Parameter( property = "stylesheet", defaultValue = "java" )
1390     private String stylesheet;
1391 
1392     /**
1393      * Specifies the path of an alternate HTML stylesheet file.
1394      * <br/>
1395      * The <code>stylesheetfile</code> could be an absolute File path.
1396      * <br/>
1397      * Since 2.6, it could be also be a path from a resource in the current project source directories
1398      * (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
1399      * or from a resource in the Javadoc plugin dependencies, for instance:
1400      * <pre>
1401      * &lt;stylesheetfile&gt;path/to/your/resource/yourstylesheet.css&lt;/stylesheetfile&gt;
1402      * </pre>
1403      * Where <code>path/to/your/resource/yourstylesheet.css</code> could be in <code>src/main/javadoc</code>.
1404      * <pre>
1405      * &lt;build&gt;
1406      *   &lt;plugins&gt;
1407      *     &lt;plugin&gt;
1408      *       &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
1409      *       &lt;artifactId&gt;maven-javadoc-plugin&lt;/artifactId&gt;
1410      *       &lt;configuration&gt;
1411      *         &lt;stylesheetfile&gt;path/to/your/resource/yourstylesheet.css&lt;/stylesheetfile&gt;
1412      *         ...
1413      *       &lt;/configuration&gt;
1414      *       &lt;dependencies&gt;
1415      *         &lt;dependency&gt;
1416      *           &lt;groupId&gt;groupId&lt;/groupId&gt;
1417      *           &lt;artifactId&gt;artifactId&lt;/artifactId&gt;
1418      *           &lt;version&gt;version&lt;/version&gt;
1419      *         &lt;/dependency&gt;
1420      *       &lt;/dependencies&gt;
1421      *     &lt;/plugin&gt;
1422      *     ...
1423      *   &lt;plugins&gt;
1424      * &lt;/build&gt;
1425      * </pre>
1426      * Where <code>path/to/your/resource/yourstylesheet.css</code> is defined in the
1427      * <code>groupId:artifactId:version</code> javadoc plugin dependency.
1428      * <br/>
1429      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#stylesheetfile">
1430      * stylesheetfile</a>.
1431      */
1432     @Parameter( property = "stylesheetfile" )
1433    private String stylesheetfile;
1434 
1435     /**
1436      * Specifies the class file that starts the taglet used in generating the documentation for that tag.
1437      * <br/>
1438      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#taglet">taglet</a>.
1439      * <br/>
1440      * Since
1441      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java 1.4</a>.
1442      */
1443     @Parameter( property = "taglet" )
1444     private String taglet;
1445 
1446     /**
1447      * Specifies the Taglet artifact containing the taglet class files (.class).
1448      * <br/>
1449      * See
1450      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
1451      * <br/>
1452      * Example:
1453      * <pre>
1454      * &lt;taglets&gt;
1455      *   &lt;taglet&gt;
1456      *     &lt;tagletClass&gt;com.sun.tools.doclets.ToDoTaglet&lt;/tagletClass&gt;
1457      *   &lt;/taglet&gt;
1458      *   &lt;taglet&gt;
1459      *     &lt;tagletClass&gt;package.to.AnotherTagletClass&lt;/tagletClass&gt;
1460      *   &lt;/taglet&gt;
1461      *   ...
1462      * &lt;/taglets&gt;
1463      * &lt;tagletArtifact&gt;
1464      *   &lt;groupId&gt;group-Taglet&lt;/groupId&gt;
1465      *   &lt;artifactId&gt;artifact-Taglet&lt;/artifactId&gt;
1466      *   &lt;version&gt;version-Taglet&lt;/version&gt;
1467      * &lt;/tagletArtifact&gt;
1468      * </pre>
1469      * <br/>
1470      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/TagletArtifact.html">Javadoc</a>.
1471      * <br/>
1472      *
1473      * @since 2.1
1474      */
1475     @Parameter( property = "tagletArtifact" )
1476     private TagletArtifact tagletArtifact;
1477 
1478     /**
1479      * Specifies several Taglet artifacts containing the taglet class files (.class). These taglets class names will be
1480      * auto-detect and so no need to specify them.
1481      * <br/>
1482      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#taglet">taglet</a>.
1483      * <br/>
1484      * See
1485      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
1486      * <br/>
1487      * Example:
1488      * <pre>
1489      * &lt;tagletArtifacts&gt;
1490      *   &lt;tagletArtifact&gt;
1491      *     &lt;groupId&gt;group-Taglet&lt;/groupId&gt;
1492      *     &lt;artifactId&gt;artifact-Taglet&lt;/artifactId&gt;
1493      *     &lt;version&gt;version-Taglet&lt;/version&gt;
1494      *   &lt;/tagletArtifact&gt;
1495      *   ...
1496      * &lt;/tagletArtifacts&gt;
1497      * </pre>
1498      * <br/>
1499      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/TagletArtifact.html">Javadoc</a>.
1500      * <br/>
1501      *
1502      * @since 2.5
1503      */
1504     @Parameter( property = "tagletArtifacts" )
1505     private TagletArtifact[] tagletArtifacts;
1506 
1507     /**
1508      * Specifies the search paths for finding taglet class files (.class). The <code>tagletpath</code> can contain
1509      * multiple paths by separating them with a colon (<code>:</code>) or a semi-colon (<code>;</code>).
1510      * <br/>
1511      * See
1512      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
1513      * <br/>
1514      * Since
1515      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java 1.4</a>.
1516      */
1517     @Parameter( property = "tagletpath" )
1518     private String tagletpath;
1519 
1520     /**
1521      * Enables the Javadoc tool to interpret multiple taglets.
1522      * <br/>
1523      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#taglet">taglet</a>.
1524      * <br/>
1525      * See
1526      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
1527      * <br/>
1528      * Example:
1529      * <pre>
1530      * &lt;taglets&gt;
1531      *   &lt;taglet&gt;
1532      *     &lt;tagletClass&gt;com.sun.tools.doclets.ToDoTaglet&lt;/tagletClass&gt;
1533      *     &lt;!--&lt;tagletpath&gt;/home/taglets&lt;/tagletpath&gt;--&gt;
1534      *     &lt;tagletArtifact&gt;
1535      *       &lt;groupId&gt;group-Taglet&lt;/groupId&gt;
1536      *       &lt;artifactId&gt;artifact-Taglet&lt;/artifactId&gt;
1537      *       &lt;version&gt;version-Taglet&lt;/version&gt;
1538      *     &lt;/tagletArtifact&gt;
1539      *   &lt;/taglet&gt;
1540      * &lt;/taglets&gt;
1541      * </pre>
1542      * <br/>
1543      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/Taglet.html">Javadoc</a>.
1544      * <br/>
1545      *
1546      * @since 2.1
1547      */
1548     @Parameter( property = "taglets" )
1549     private Taglet[] taglets;
1550 
1551     /**
1552      * Enables the Javadoc tool to interpret a simple, one-argument custom block tag tagname in doc comments.
1553      * <br/>
1554      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tag">tag</a>.
1555      * <br/>
1556      * Since
1557      * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java 1.4</a>.
1558      * <br/>
1559      * Example:
1560      * <pre>
1561      * &lt;tags&gt;
1562      *   &lt;tag&gt;
1563      *     &lt;name&gt;todo&lt;/name&gt;
1564      *     &lt;placement&gt;a&lt;/placement&gt;
1565      *     &lt;head&gt;To Do:&lt;/head&gt;
1566      *   &lt;/tag&gt;
1567      * &lt;/tags&gt;
1568      * </pre>
1569      * <b>Note</b>: the placement should be a combinaison of Xaoptcmf letters:
1570      * <ul>
1571      * <li><b><code>X</code></b> (disable tag)</li>
1572      * <li><b><code>a</code></b> (all)</li>
1573      * <li><b><code>o</code></b> (overview)</li>
1574      * <li><b><code>p</code></b> (packages)</li>
1575      * <li><b><code>t</code></b> (types, that is classes and interfaces)</li>
1576      * <li><b><code>c</code></b> (constructors)</li>
1577      * <li><b><code>m</code></b> (methods)</li>
1578      * <li><b><code>f</code></b> (fields)</li>
1579      * </ul>
1580      * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/Tag.html">Javadoc</a>.
1581      * <br/>
1582      */
1583     @Parameter( property = "tags" )
1584     private Tag[] tags;
1585 
1586     /**
1587      * Specifies the top text to be placed at the top of each output file.
1588      * <br/>
1589      * See <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6227616">6227616</a>.
1590      * <br/>
1591      * Since Java 6.0
1592      *
1593      * @since 2.4
1594      */
1595     @Parameter( property = "top" )
1596     private String top;
1597 
1598     /**
1599      * Includes one "Use" page for each documented class and package.
1600      * <br/>
1601      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#use">use</a>.
1602      * <br/>
1603      */
1604     @Parameter( property = "use", defaultValue = "true" )
1605     private boolean use;
1606 
1607     /**
1608      * Includes the version text in the generated docs.
1609      * <br/>
1610      * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#version">version</a>.
1611      * <br/>
1612      */
1613     @Parameter( property = "version", defaultValue = "true" )
1614     private boolean version;
1615 
1616     /**
1617      * Specifies the title to be placed in the HTML title tag.
1618      * <br/>
1619      * See
1620      * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#windowtitle">windowtitle</a>.
1621      * <br/>
1622      */
1623     @Parameter( property = "windowtitle", defaultValue = "${project.name} ${project.version} API" )
1624     private String windowtitle;
1625 
1626     /**
1627      * Whether dependency -sources jars should be resolved and included as source paths for javadoc generation.
1628      * This is useful when creating javadocs for a distribution project.
1629      *
1630      * @since 2.7
1631      */
1632     @Parameter( defaultValue = "false" )
1633     private boolean includeDependencySources;
1634 
1635     /**
1636      * Directory where unpacked project sources / test-sources should be cached.
1637      *
1638      * @see #includeDependencySources
1639      * @since 2.7
1640      */
1641     @Parameter( defaultValue = "${project.build.directory}/distro-javadoc-sources" )
1642     private File sourceDependencyCacheDir;
1643 
1644     /**
1645      * Whether to include transitive dependencies in the list of dependency -sources jars to include
1646      * in this javadoc run.
1647      *
1648      * @see #includeDependencySources
1649      * @since 2.7
1650      */
1651     @Parameter( defaultValue = "false" )
1652     private boolean includeTransitiveDependencySources;
1653 
1654     /**
1655      * List of included dependency-source patterns. Example: <code>org.apache.maven:*</code>
1656      *
1657      * @see #includeDependencySources
1658      * @since 2.7
1659      */
1660     @Parameter
1661     private List<String> dependencySourceIncludes;
1662 
1663     /**
1664      * List of excluded dependency-source patterns. Example: <code>org.apache.maven.shared:*</code>
1665      *
1666      * @see #includeDependencySources
1667      * @since 2.7
1668      */
1669     @Parameter
1670     private List<String> dependencySourceExcludes;
1671 
1672     /**
1673      * Directory into which assembled {@link JavadocOptions} instances will be written before they
1674      * are added to javadoc resources bundles.
1675      *
1676      * @since 2.7
1677      */
1678     @Parameter( defaultValue = "${project.build.directory}/javadoc-bundle-options", readonly = true )
1679     private File javadocOptionsDir;
1680 
1681     /**
1682      * Transient variable to allow lazy-resolution of javadoc bundles from dependencies, so they can
1683      * be used at various points in the javadoc generation process.
1684      *
1685      * @since 2.7
1686      */
1687     private transient List<JavadocBundle> dependencyJavadocBundles;
1688 
1689     /**
1690      * Capability to add additional dependencies to the javadoc classpath.
1691      * Example:
1692      * <pre>
1693      * &lt;additionalDependencies&gt;
1694      *   &lt;additionalDependency&gt;
1695      *     &lt;groupId&gt;geronimo-spec&lt;/groupId&gt;
1696      *     &lt;artifactId&gt;geronimo-spec-jta&lt;/artifactId&gt;
1697      *     &lt;version&gt;1.0.1B-rc4:&lt;/version&gt;
1698      *   &lt;/additionalDependency&gt;
1699      * &lt;/additionalDependencies&gt;
1700      * </pre>
1701      *
1702      * @since 2.8.1
1703      */
1704     @Parameter
1705     private List<AdditionalDependency> additionalDependencies;
1706 
1707     /**
1708      * Include filters on the source files. Default is **\/\*.java.
1709      * These are ignored if you specify subpackages or subpackage excludes.
1710      */
1711     @Parameter
1712     private List<String> sourceFileIncludes;
1713 
1714     /**
1715      * exclude filters on the source files.
1716      * These are ignored if you specify subpackages or subpackage excludes.
1717      */
1718     @Parameter
1719     private List<String> sourceFileExcludes;
1720 
1721     /**
1722      * To apply the security fix on generated javadoc see http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-1571
1723      * @since 2.9.1
1724      */
1725     @Parameter( defaultValue = "true", property = "maven.javadoc.applyJavadocSecurityFix" )
1726     private boolean applyJavadocSecurityFix = true;
1727     
1728     /**
1729      * <p>
1730      * Specify the requirements for this jdk toolchain.
1731      * This overrules the toolchain selected by the maven-toolchain-plugin.
1732      * </p>
1733      * <strong>note:</strong> requires at least Maven 3.3.1
1734      * 
1735      * @since 3.0.0
1736      */
1737     @Parameter
1738     private Map<String, String> jdkToolchain;
1739 
1740     // ----------------------------------------------------------------------
1741     // static
1742     // ----------------------------------------------------------------------
1743 
1744     static
1745     {
1746         DEFAULT_JAVA_API_LINKS.put( "api_1.5", "https://docs.oracle.com/javase/1.5.0/docs/api/" );
1747         DEFAULT_JAVA_API_LINKS.put( "api_1.6", "https://docs.oracle.com/javase/6/docs/api/" );
1748         DEFAULT_JAVA_API_LINKS.put( "api_1.7", "https://docs.oracle.com/javase/7/docs/api/" );
1749         DEFAULT_JAVA_API_LINKS.put( "api_1.8", "https://docs.oracle.com/javase/8/docs/api/" );
1750         DEFAULT_JAVA_API_LINKS.put( "api_9",   "https://docs.oracle.com/javase/9/docs/api/" );
1751     }
1752 
1753     // ----------------------------------------------------------------------
1754     // protected methods
1755     // ----------------------------------------------------------------------
1756 
1757     /**
1758      * Indicates whether this goal is flagged with <code>@aggregator</code>.
1759      *
1760      * @return <code>true</code> if the goal is designed as an aggregator, <code>false</code> otherwise.
1761      * @see AggregatorJavadocReport
1762      * @see AggregatorTestJavadocReport
1763      */
1764     protected boolean isAggregator()
1765     {
1766         return false;
1767     }
1768 
1769     /**
1770      * Indicates whether this goal generates documentation for the <code>Java Test code</code>.
1771      *
1772      * @return <code>true</code> if the goal generates Test Javadocs, <code>false</code> otherwise.
1773      */
1774     protected boolean isTest()
1775     {
1776         return false;
1777     }
1778 
1779     /**
1780      * @return the output directory
1781      */
1782     protected String getOutputDirectory()
1783     {
1784         return outputDirectory.getAbsoluteFile().toString();
1785     }
1786 
1787     protected MavenProject getProject()
1788     {
1789         return project;
1790     }
1791 
1792     /**
1793      * @param p not null maven project
1794      * @return the list of directories where compiled classes are placed for the given project. These dirs are
1795      *         added in the javadoc classpath.
1796      */
1797     protected List<File> getProjectBuildOutputDirs( MavenProject p )
1798     {
1799         if ( StringUtils.isEmpty( p.getBuild().getOutputDirectory() ) )
1800         {
1801             return Collections.emptyList();
1802         }
1803 
1804         return Collections.singletonList( new File( p.getBuild().getOutputDirectory() ) );
1805     }
1806 
1807     /**
1808      * @param p not null maven project
1809      * @return the list of source paths for the given project
1810      */
1811     protected List<String> getProjectSourceRoots( MavenProject p )
1812     {
1813         if ( "pom".equals( p.getPackaging().toLowerCase() ) )
1814         {
1815             return Collections.emptyList();
1816         }
1817 
1818         return ( p.getCompileSourceRoots() == null
1819             ? Collections.<String>emptyList()
1820             : new LinkedList<>( p.getCompileSourceRoots() ) );
1821     }
1822 
1823     /**
1824      * @param p not null maven project
1825      * @return the list of source paths for the execution project of the given project
1826      */
1827     protected List<String> getExecutionProjectSourceRoots( MavenProject p )
1828     {
1829         if ( "pom".equals( p.getExecutionProject().getPackaging().toLowerCase() ) )
1830         {
1831             return Collections.emptyList();
1832         }
1833 
1834         return ( p.getExecutionProject().getCompileSourceRoots() == null
1835             ? Collections.<String>emptyList()
1836             : new LinkedList<>( p.getExecutionProject().getCompileSourceRoots() ) );
1837     }
1838 
1839     /**
1840      * @return the current javadoc directory
1841      */
1842     protected File getJavadocDirectory()
1843     {
1844         return javadocDirectory;
1845     }
1846 
1847     /**
1848      * @return the doclint specific checks configuration
1849      */
1850     protected String getDoclint()
1851     {
1852         return doclint;
1853     }
1854 
1855     /**
1856      * @return the title to be placed near the top of the overview summary file
1857      */
1858     protected String getDoctitle()
1859     {
1860         return doctitle;
1861     }
1862 
1863     /**
1864      * @return the overview documentation file from the user parameter or from the <code>javadocdirectory</code>
1865      */
1866     protected File getOverview()
1867     {
1868         return overview;
1869     }
1870 
1871     /**
1872      * @return the title to be placed in the HTML title tag
1873      */
1874     protected String getWindowtitle()
1875     {
1876         return windowtitle;
1877     }
1878 
1879     /**
1880      * @return the charset attribute or the value of {@link #getDocencoding()} if <code>null</code>.
1881      */
1882     private String getCharset()
1883     {
1884         return ( StringUtils.isEmpty( charset ) ) ? getDocencoding() : charset;
1885     }
1886 
1887     /**
1888      * @return the docencoding attribute or <code>UTF-8</code> if <code>null</code>.
1889      */
1890     private String getDocencoding()
1891     {
1892         return ( StringUtils.isEmpty( docencoding ) ) ? ReaderFactory.UTF_8 : docencoding;
1893     }
1894 
1895     /**
1896      * @return the encoding attribute or the value of <code>file.encoding</code> system property if <code>null</code>.
1897      */
1898     private String getEncoding()
1899     {
1900         return ( StringUtils.isEmpty( encoding ) ) ? ReaderFactory.FILE_ENCODING : encoding;
1901     }
1902 
1903     @Override
1904     public void execute()
1905         throws MojoExecutionException, MojoFailureException
1906     {
1907         verifyRemovedParameter( "aggregator" );
1908         verifyRemovedParameter( "proxyHost" );
1909         verifyRemovedParameter( "proxyPort" );
1910         verifyReplacedParameter( "additionalparam", "additionalOptions" );
1911 
1912         doExecute();
1913     }
1914 
1915     abstract void doExecute() throws MojoExecutionException, MojoFailureException;
1916 
1917     protected final void verifyRemovedParameter( String paramName )
1918     {
1919         Object pluginConfiguration = mojo.getConfiguration();
1920         if ( pluginConfiguration instanceof Xpp3Dom )
1921         {
1922             Xpp3Dom configDom = (Xpp3Dom) pluginConfiguration;
1923 
1924             if ( configDom.getChild( paramName ) != null )
1925             {
1926                 throw new IllegalArgumentException( "parameter '" + paramName
1927                     + "' has been removed from the plugin, please verify documentation." );
1928             }
1929         }
1930     }
1931 
1932     private void verifyReplacedParameter( String oldParamName, String newParamNew )
1933     {
1934         Object pluginConfiguration = mojo.getConfiguration();
1935         if ( pluginConfiguration instanceof Xpp3Dom )
1936         {
1937             Xpp3Dom configDom = (Xpp3Dom) pluginConfiguration;
1938 
1939             if ( configDom.getChild( oldParamName ) != null )
1940             {
1941                 throw new IllegalArgumentException( "parameter '" + oldParamName
1942                     + "' has been replaced with " + newParamNew + ", please verify documentation." );
1943             }
1944         }
1945     }
1946 
1947     /**
1948      * The <a href="package-summary.html">package documentation</a> details the
1949      * Javadoc Options used by this Plugin.
1950      *
1951      * @param unusedLocale the wanted locale (actually unused).
1952      * @throws MavenReportException if any
1953      */
1954     protected void executeReport( Locale unusedLocale )
1955         throws MavenReportException
1956     {
1957         if ( skip )
1958         {
1959             getLog().info( "Skipping javadoc generation" );
1960             return;
1961         }
1962 
1963         if ( isAggregator() && !project.isExecutionRoot() )
1964         {
1965             return;
1966         }
1967 
1968         if ( getLog().isDebugEnabled() )
1969         {
1970             this.debug = true;
1971         }
1972 
1973         // NOTE: Always generate this file, to allow javadocs from modules to be aggregated via
1974         // useDependencySources in a distro module build.
1975         try
1976         {
1977             buildJavadocOptions();
1978         }
1979         catch ( IOException e )
1980         {
1981             throw new MavenReportException( "Failed to generate javadoc options file: " + e.getMessage(), e );
1982         }
1983 
1984         Map<String, Collection<String>> sourcePaths = getSourcePaths();
1985         
1986         Collection<String> collectedSourcePaths = collect( sourcePaths.values() );
1987         
1988         List<String> files = getFiles( collectedSourcePaths );
1989         if ( !canGenerateReport( files ) )
1990         {
1991             return;
1992         }
1993 
1994         List<String> packageNames = getPackageNames( collectedSourcePaths, files );
1995         List<String> filesWithUnnamedPackages = getFilesWithUnnamedPackages( collectedSourcePaths, files );
1996 
1997         // ----------------------------------------------------------------------
1998         // Find the javadoc executable and version
1999         // ----------------------------------------------------------------------
2000 
2001         String jExecutable;
2002         try
2003         {
2004             jExecutable = getJavadocExecutable();
2005         }
2006         catch ( IOException e )
2007         {
2008             throw new MavenReportException( "Unable to find javadoc command: " + e.getMessage(), e );
2009         }
2010         setFJavadocVersion( new File( jExecutable ) );
2011 
2012         // ----------------------------------------------------------------------
2013         // Javadoc output directory as File
2014         // ----------------------------------------------------------------------
2015 
2016         File javadocOutputDirectory = new File( getOutputDirectory() );
2017         if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.isDirectory() )
2018         {
2019             throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not a directory." );
2020         }
2021         if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.canWrite() )
2022         {
2023             throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not writable." );
2024         }
2025         javadocOutputDirectory.mkdirs();
2026 
2027         // ----------------------------------------------------------------------
2028         // Copy all resources
2029         // ----------------------------------------------------------------------
2030 
2031         copyAllResources( javadocOutputDirectory );
2032 
2033         // ----------------------------------------------------------------------
2034         // Create command line for Javadoc
2035         // ----------------------------------------------------------------------
2036 
2037         Commandline cmd = new Commandline();
2038         cmd.getShell().setQuotedArgumentsEnabled( false ); // for Javadoc JVM args
2039         cmd.setWorkingDirectory( javadocOutputDirectory.getAbsolutePath() );
2040         cmd.setExecutable( jExecutable );
2041 
2042         // ----------------------------------------------------------------------
2043         // Wrap Javadoc JVM args
2044         // ----------------------------------------------------------------------
2045 
2046         addMemoryArg( cmd, "-Xmx", this.maxmemory );
2047         addMemoryArg( cmd, "-Xms", this.minmemory );
2048         addProxyArg( cmd );
2049 
2050         if ( StringUtils.isNotEmpty( additionalJOption ) )
2051         {
2052             cmd.createArg().setValue( additionalJOption );
2053         }
2054 
2055         if ( additionalJOptions != null && additionalJOptions.length != 0 )
2056         {
2057             for ( String jo : additionalJOptions )
2058             {
2059                 cmd.createArg().setValue( jo );
2060             }
2061         }
2062 
2063         List<String> arguments = new ArrayList<>();
2064 
2065         // ----------------------------------------------------------------------
2066         // Wrap Javadoc options
2067         // ----------------------------------------------------------------------
2068 
2069         addJavadocOptions( javadocOutputDirectory, arguments, sourcePaths );
2070 
2071         // ----------------------------------------------------------------------
2072         // Wrap Standard doclet Options
2073         // ----------------------------------------------------------------------
2074 
2075         if ( StringUtils.isEmpty( doclet ) || useStandardDocletOptions )
2076         {
2077             addStandardDocletOptions( javadocOutputDirectory, arguments );
2078         }
2079 
2080         // ----------------------------------------------------------------------
2081         // Write options file and include it in the command line
2082         // ----------------------------------------------------------------------
2083 
2084         if ( arguments.size() > 0 )
2085         {
2086             addCommandLineOptions( cmd, arguments, javadocOutputDirectory );
2087         }
2088 
2089         // ----------------------------------------------------------------------
2090         // Write packages file and include it in the command line
2091         // ----------------------------------------------------------------------
2092 
2093         // MJAVADOC-365 if includes/excludes are specified, these take precedence over the default
2094         // package-based mode and force javadoc into file-based mode unless subpackages are
2095         // specified. Subpackages take precedence over file-based include/excludes. Why? Because
2096         // getFiles(...) returns an empty list when subpackages are specified.
2097         boolean includesExcludesActive =
2098             ( sourceFileIncludes != null && !sourceFileIncludes.isEmpty() )
2099                 || ( sourceFileExcludes != null && !sourceFileExcludes.isEmpty() );
2100         if ( includesExcludesActive && !StringUtils.isEmpty( subpackages ) )
2101         {
2102             getLog().warn( "sourceFileIncludes and sourceFileExcludes have no effect when subpackages are specified!" );
2103             includesExcludesActive = false;
2104         }
2105         if ( !packageNames.isEmpty() && !includesExcludesActive )
2106         {
2107             addCommandLinePackages( cmd, javadocOutputDirectory, packageNames );
2108 
2109             // ----------------------------------------------------------------------
2110             // Write argfile file and include it in the command line
2111             // ----------------------------------------------------------------------
2112 
2113             if ( !filesWithUnnamedPackages.isEmpty() )
2114             {
2115                 addCommandLineArgFile( cmd, javadocOutputDirectory, filesWithUnnamedPackages );
2116             }
2117         }
2118         else
2119         {
2120             // ----------------------------------------------------------------------
2121             // Write argfile file and include it in the command line
2122             // ----------------------------------------------------------------------
2123 
2124             if ( !files.isEmpty() )
2125             {
2126                 addCommandLineArgFile( cmd, javadocOutputDirectory, files );
2127             }
2128         }
2129 
2130         // ----------------------------------------------------------------------
2131         // Execute command line
2132         // ----------------------------------------------------------------------
2133 
2134         executeJavadocCommandLine( cmd, javadocOutputDirectory );
2135 
2136         // delete generated javadoc files only if no error and no debug mode
2137         // [MJAVADOC-336] Use File.delete() instead of File.deleteOnExit() to
2138         // prevent these files from making their way into archives.
2139         if ( !debug )
2140         {
2141             for ( int i = 0; i < cmd.getArguments().length; i++ )
2142             {
2143                 String arg = cmd.getArguments()[i].trim();
2144 
2145                 if ( !arg.startsWith( "@" ) )
2146                 {
2147                     continue;
2148                 }
2149 
2150                 File argFile = new File( javadocOutputDirectory, arg.substring( 1 ) );
2151                 if ( argFile.exists() )
2152                 {
2153                     argFile.delete();
2154                 }
2155             }
2156 
2157             File scriptFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
2158             if ( scriptFile.exists() )
2159             {
2160                 scriptFile.delete();
2161             }
2162         }
2163         if ( applyJavadocSecurityFix )
2164         {
2165             // finally, patch the Javadoc vulnerability in older Javadoc tools (CVE-2013-1571):
2166             try
2167             {
2168                 final int patched = fixFrameInjectionBug( javadocOutputDirectory, getDocencoding() );
2169                 if ( patched > 0 )
2170                 {
2171                     getLog().info(
2172                         String.format( "Fixed Javadoc frame injection vulnerability (CVE-2013-1571) in %d files.",
2173                                        patched ) );
2174                 }
2175             }
2176             catch ( IOException e )
2177             {
2178                 throw new MavenReportException( "Failed to patch javadocs vulnerability: " + e.getMessage(), e );
2179             }
2180         }
2181         else
2182         {
2183           getLog().info( "applying javadoc security fix has been disabled" );
2184         }
2185     }
2186 
2187     protected final Collection<String> collect( Collection<Collection<String>> sourcePaths )
2188     {
2189         Collection<String> collectedSourcePaths = new LinkedHashSet<>();
2190         for ( Collection<String> sp : sourcePaths )
2191         {
2192             collectedSourcePaths.addAll( sp );
2193         }
2194         return collectedSourcePaths;
2195     }
2196 
2197     /**
2198      * Method to get the files on the specified source paths
2199      *
2200      * @param sourcePaths a Collection that contains the paths to the source files
2201      * @return a List that contains the specific path for every source file
2202      * @throws MavenReportException {@link MavenReportException}
2203      */
2204     protected List<String> getFiles( Collection<String> sourcePaths )
2205         throws MavenReportException
2206     {
2207         List<String> files = new ArrayList<>();
2208         if ( StringUtils.isEmpty( subpackages ) )
2209         {
2210             String[] excludedPackages = getExcludedPackages();
2211 
2212             for ( String sourcePath : sourcePaths )
2213             {
2214                 File sourceDirectory = new File( sourcePath );
2215                 JavadocUtil.addFilesFromSource( files, sourceDirectory, sourceFileIncludes, sourceFileExcludes,
2216                                                 excludedPackages );
2217             }
2218         }
2219 
2220         return files;
2221     }
2222 
2223     /**
2224      * Method to get the source paths. If no source path is specified in the parameter, the compile source roots
2225      * of the project will be used.
2226      *
2227      * @return a Collection of the project absolute source paths as <code>String</code>
2228      * @throws MavenReportException {@link MavenReportException}
2229      * @see JavadocUtil#pruneDirs(MavenProject, Collection)
2230      */
2231     protected Map<String, Collection<String>> getSourcePaths()
2232         throws MavenReportException
2233     {
2234         Map<String, Collection<String>> mappedSourcePaths = new LinkedHashMap<>();
2235 
2236         if ( StringUtils.isEmpty( sourcepath ) )
2237         {
2238             
2239             Set<String> sourcePaths =
2240                 new LinkedHashSet<>( JavadocUtil.pruneDirs( project, getProjectSourceRoots( project ) ) );
2241 
2242             if ( project.getExecutionProject() != null )
2243             {
2244                 sourcePaths.addAll( JavadocUtil.pruneDirs( project, getExecutionProjectSourceRoots( project ) ) );
2245             }
2246 
2247             /*
2248              * Should be after the source path (i.e. -sourcepath '.../src/main/java;.../src/main/javadoc') and
2249              * *not* the opposite. If not, the javadoc tool always copies doc files, even if -docfilessubdirs is
2250              * not setted.
2251              */
2252             if ( getJavadocDirectory() != null )
2253             {
2254                 File javadocDir = getJavadocDirectory();
2255                 if ( javadocDir.exists() && javadocDir.isDirectory() )
2256                 {
2257                     Collection<String> l = JavadocUtil.pruneDirs( project, Collections.singletonList(
2258                         getJavadocDirectory().getAbsolutePath() ) );
2259                     sourcePaths.addAll( l );
2260                 }
2261             }
2262             mappedSourcePaths.put( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
2263                                    sourcePaths );
2264             
2265             if ( includeDependencySources )
2266             {
2267                 mappedSourcePaths.putAll( getDependencySourcePaths() );
2268             }
2269 
2270             if ( isAggregator() && project.isExecutionRoot() )
2271             {
2272                 for ( MavenProject subProject : reactorProjects )
2273                 {
2274                     if ( subProject != project )
2275                     {
2276                         Collection<String> additionalSourcePaths = new ArrayList<>();
2277 
2278                         List<String> sourceRoots = getProjectSourceRoots( subProject );
2279 
2280                         if ( subProject.getExecutionProject() != null )
2281                         {
2282                             sourceRoots.addAll( getExecutionProjectSourceRoots( subProject ) );
2283                         }
2284 
2285                         ArtifactHandler artifactHandler = subProject.getArtifact().getArtifactHandler();
2286                         if ( "java".equals( artifactHandler.getLanguage() ) )
2287                         {
2288                             additionalSourcePaths.addAll( JavadocUtil.pruneDirs( subProject, sourceRoots ) );
2289                         }
2290 
2291                         if ( getJavadocDirectory() != null )
2292                         {
2293                             String javadocDirRelative =
2294                                 PathUtils.toRelative( project.getBasedir(),
2295                                                       getJavadocDirectory().getAbsolutePath() );
2296                             File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
2297                             if ( javadocDir.exists() && javadocDir.isDirectory() )
2298                             {
2299                                 Collection<String> l = JavadocUtil.pruneDirs( subProject, Collections.singletonList(
2300                                         javadocDir.getAbsolutePath() ) );
2301                                 additionalSourcePaths.addAll( l );
2302                             }
2303                         }
2304                         mappedSourcePaths.put( ArtifactUtils.versionlessKey( subProject.getGroupId(),
2305                                                                              subProject.getArtifactId() ),
2306                                                additionalSourcePaths );
2307                     }
2308                 }
2309             }
2310         }
2311         else
2312         {
2313             Collection<String> sourcePaths = new ArrayList<>( Arrays.asList( JavadocUtil.splitPath( sourcepath ) ) );
2314             sourcePaths = JavadocUtil.pruneDirs( project, sourcePaths );
2315             if ( getJavadocDirectory() != null )
2316             {
2317                 Collection<String> l = JavadocUtil.pruneDirs( project, Collections.singletonList(
2318                     getJavadocDirectory().getAbsolutePath() ) );
2319                 sourcePaths.addAll( l );
2320             }
2321             mappedSourcePaths.put( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
2322                                    sourcePaths );
2323         }
2324 
2325         return mappedSourcePaths;
2326     }
2327 
2328     /**
2329      * Override this method to customize the configuration for resolving dependency sources. The default
2330      * behavior enables the resolution of -sources jar files.
2331      * @param config {@link SourceResolverConfig}
2332      * @return {@link SourceResolverConfig}
2333      */
2334     protected SourceResolverConfig configureDependencySourceResolution( final SourceResolverConfig config )
2335     {
2336         return config.withCompileSources();
2337     }
2338 
2339     /**
2340      * Resolve dependency sources so they can be included directly in the javadoc process. To customize this,
2341      * override {@link AbstractJavadocMojo#configureDependencySourceResolution(SourceResolverConfig)}.
2342      * @return List of source paths.
2343      * @throws MavenReportException {@link MavenReportException}
2344      */
2345     protected final Map<String, Collection<String>> getDependencySourcePaths()
2346         throws MavenReportException
2347     {
2348         try
2349         {
2350             if ( sourceDependencyCacheDir.exists() )
2351             {
2352                 FileUtils.forceDelete( sourceDependencyCacheDir );
2353                 sourceDependencyCacheDir.mkdirs();
2354             }
2355         }
2356         catch ( IOException e )
2357         {
2358             throw new MavenReportException(
2359                 "Failed to delete cache directory: " + sourceDependencyCacheDir + "\nReason: " + e.getMessage(), e );
2360         }
2361 
2362         final SourceResolverConfig config = getDependencySourceResolverConfig();
2363 
2364         final List<TransformableFilter> andFilters = new ArrayList<>();
2365 
2366         final List<String> dependencyIncludes = dependencySourceIncludes;
2367         final List<String> dependencyExcludes = dependencySourceExcludes;
2368 
2369         if ( !includeTransitiveDependencySources || isNotEmpty( dependencyIncludes ) || isNotEmpty(
2370             dependencyExcludes ) )
2371         {
2372             if ( !includeTransitiveDependencySources )
2373             {
2374                 andFilters.add( createDependencyArtifactFilter() );
2375             }
2376 
2377             if ( isNotEmpty( dependencyIncludes ) )
2378             {
2379                 andFilters.add( new PatternInclusionsFilter( dependencyIncludes ) );
2380             }
2381 
2382             if ( isNotEmpty( dependencyExcludes ) )
2383             {
2384                 andFilters.add( new PatternExclusionsFilter( dependencyExcludes ) );
2385             }
2386 
2387             config.withFilter( new AndFilter( andFilters ) );
2388         }
2389 
2390         try
2391         {
2392             return resourceResolver.resolveDependencySourcePaths( config );
2393         }
2394         catch ( final ArtifactResolutionException e )
2395         {
2396             throw new MavenReportException(
2397                 "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e );
2398         }
2399         catch ( final ArtifactNotFoundException e )
2400         {
2401             throw new MavenReportException(
2402                 "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e );
2403         }
2404     }
2405 
2406     /**
2407      * Returns a ArtifactFilter that only includes direct dependencies of this project
2408      * (verified via groupId and artifactId).
2409      *
2410      * @return
2411      */
2412     private TransformableFilter createDependencyArtifactFilter()
2413     {
2414         Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
2415 
2416         List<String> artifactPatterns = new ArrayList<>( dependencyArtifacts.size() );
2417         for ( Artifact artifact : dependencyArtifacts )
2418         {
2419             artifactPatterns.add( artifact.getGroupId() + ":" + artifact.getArtifactId() );
2420         }
2421 
2422         return new PatternInclusionsFilter( artifactPatterns );
2423     }
2424 
2425     /**
2426      * Construct a SourceResolverConfig for resolving dependency sources and resources in a consistent
2427      * way, so it can be reused for both source and resource resolution.
2428      *
2429      * @since 2.7
2430      */
2431     private SourceResolverConfig getDependencySourceResolverConfig()
2432     {
2433         return configureDependencySourceResolution(
2434                         new SourceResolverConfig( project, session.getProjectBuildingRequest(),
2435                                                   sourceDependencyCacheDir ).withReactorProjects( reactorProjects ) );
2436     }
2437 
2438     /**
2439      * Method that indicates whether the javadoc can be generated or not. If the project does not contain any source
2440      * files and no subpackages are specified, the plugin will terminate.
2441      *
2442      * @param files the project files
2443      * @return a boolean that indicates whether javadoc report can be generated or not
2444      */
2445     protected boolean canGenerateReport( List<String> files )
2446     {
2447         boolean canGenerate = true;
2448 
2449         if ( files.isEmpty() && StringUtils.isEmpty( subpackages ) )
2450         {
2451             canGenerate = false;
2452         }
2453 
2454         return canGenerate;
2455     }
2456 
2457     // ----------------------------------------------------------------------
2458     // private methods
2459     // ----------------------------------------------------------------------
2460 
2461     /**
2462      * Method to get the excluded source files from the javadoc and create the argument string
2463      * that will be included in the javadoc commandline execution.
2464      *
2465      * @param sourcePaths the collection of paths to the source files
2466      * @return a String that contains the exclude argument that will be used by javadoc
2467      * @throws MavenReportException
2468      */
2469     private String getExcludedPackages( Collection<String> sourcePaths )
2470         throws MavenReportException
2471     {
2472         List<String> excludedNames = null;
2473 
2474         if ( StringUtils.isNotEmpty( sourcepath ) && StringUtils.isNotEmpty( subpackages ) )
2475         {
2476             String[] excludedPackages = getExcludedPackages();
2477             String[] subpackagesList = subpackages.split( "[:]" );
2478 
2479             excludedNames = JavadocUtil.getExcludedNames( sourcePaths, subpackagesList, excludedPackages );
2480         }
2481 
2482         String excludeArg = "";
2483         if ( StringUtils.isNotEmpty( subpackages ) && excludedNames != null )
2484         {
2485             // add the excludedpackage names
2486             excludeArg = StringUtils.join( excludedNames.iterator(), ":" );
2487         }
2488 
2489         return excludeArg;
2490     }
2491 
2492     /**
2493      * Method to format the specified source paths that will be accepted by the javadoc tool.
2494      *
2495      * @param sourcePaths the list of paths to the source files that will be included in the javadoc.
2496      * @return a String that contains the formatted source path argument, separated by the System pathSeparator
2497      *         string (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
2498      * @see File#pathSeparator
2499      */
2500     private String getSourcePath( Collection<String> sourcePaths )
2501     {
2502         String sourcePath = null;
2503 
2504         if ( StringUtils.isEmpty( subpackages ) || StringUtils.isNotEmpty( sourcepath ) )
2505         {
2506             sourcePath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
2507         }
2508 
2509         return sourcePath;
2510     }
2511 
2512     /**
2513      * Method to get the packages specified in the <code>excludePackageNames</code> parameter. The packages are split
2514      * with ',', ':', or ';' and then formatted.
2515      *
2516      * @return an array of String objects that contain the package names
2517      * @throws MavenReportException
2518      */
2519     private String[] getExcludedPackages()
2520         throws MavenReportException
2521     {
2522         Set<String> excluded = new LinkedHashSet<>();
2523 
2524         if ( includeDependencySources )
2525         {
2526             try
2527             {
2528                 resolveDependencyBundles();
2529             }
2530             catch ( IOException e )
2531             {
2532                 throw new MavenReportException(
2533                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
2534             }
2535 
2536             if ( isNotEmpty( dependencyJavadocBundles ) )
2537             {
2538                 for ( JavadocBundle bundle : dependencyJavadocBundles )
2539                 {
2540                     JavadocOptions options = bundle.getOptions();
2541                     if ( options != null && isNotEmpty( options.getExcludePackageNames() ) )
2542                     {
2543                         excluded.addAll( options.getExcludePackageNames() );
2544                     }
2545                 }
2546             }
2547         }
2548 
2549         // for the specified excludePackageNames
2550         if ( StringUtils.isNotEmpty( excludePackageNames ) )
2551         {
2552             List<String> packageNames = Arrays.asList( excludePackageNames.split( "[,:;]" ) );
2553             excluded.addAll( trimValues( packageNames ) );
2554         }
2555 
2556         String[] result = new String[excluded.size()];
2557         if ( isNotEmpty( excluded ) )
2558         {
2559             int idx = 0;
2560             for ( String exclude : excluded )
2561             {
2562                 result[idx] = exclude.replace( '.', File.separatorChar );
2563                 idx++;
2564             }
2565         }
2566 
2567         return result;
2568     }
2569 
2570     private static List<String> trimValues( List<String> items )
2571     {
2572         List<String> result = new ArrayList<>( items.size() );
2573         for ( String item : items )
2574         {
2575             String trimmed = item.trim();
2576             if ( StringUtils.isEmpty( trimmed ) )
2577             {
2578                 continue;
2579             }
2580             result.add( trimmed );
2581         }
2582         return result;
2583     }
2584 
2585     /**
2586      * Method that gets the classpath and modulepath elements that will be specified in the javadoc
2587      * <code>-classpath</code> and <code>--module-path</code> parameter. 
2588      * Since we have all the sources of the current reactor, it is sufficient to consider the
2589      * dependencies of the reactor modules, excluding the module artifacts which may not yet be available
2590      * when the reactor project is built for the first time.
2591      *
2592      * @return all classpath elements
2593      * @throws MavenReportException if any.
2594      */
2595     private List<File> getPathElements()
2596         throws MavenReportException
2597     {
2598         List<File> classpathElements = new ArrayList<>();
2599         Map<String, Artifact> compileArtifactMap = new HashMap<>();
2600 
2601         if ( isTest() )
2602         {
2603             classpathElements.addAll( getProjectBuildOutputDirs( project ) );
2604         }
2605         
2606         populateCompileArtifactMap( compileArtifactMap, project.getArtifacts() );
2607 
2608         if ( isAggregator() && project.isExecutionRoot() )
2609         {
2610             List<String> reactorArtifacts = new ArrayList<>();
2611             for ( MavenProject p : reactorProjects )
2612             {
2613                 reactorArtifacts.add( p.getGroupId() + ':' + p.getArtifactId() );
2614             }
2615             
2616             TransformableFilter dependencyFilter = new AndFilter( Arrays.asList( 
2617                                                                      new PatternExclusionsFilter( reactorArtifacts ),
2618                                                                      getDependencyScopeFilter() ) );
2619 
2620             for ( MavenProject subProject : reactorProjects )
2621             {
2622                 if ( subProject != project )
2623                 {
2624                     classpathElements.addAll( getProjectBuildOutputDirs( subProject ) );
2625 
2626                     try
2627                     {
2628                         StringBuilder sb = new StringBuilder();
2629 
2630                         sb.append( "Compiled artifacts for " );
2631                         sb.append( subProject.getGroupId() ).append( ":" );
2632                         sb.append( subProject.getArtifactId() ).append( ":" );
2633                         sb.append( subProject.getVersion() ).append( '\n' );
2634 
2635                         ProjectBuildingRequest buildingRequest = session.getProjectBuildingRequest();
2636                         buildingRequest =
2637                             buildingRequest.setRemoteRepositories( subProject.getRemoteArtifactRepositories() );
2638                         
2639                         for ( ArtifactResult artifactResult
2640                                     : dependencyResolver.resolveDependencies( buildingRequest,
2641                                                                               subProject.getDependencies(),
2642                                                                               null,
2643                                                                               dependencyFilter ) )
2644                         {
2645                             populateCompileArtifactMap( compileArtifactMap,
2646                                                         Collections.singletonList( artifactResult.getArtifact() ) );
2647                             
2648                             sb.append( artifactResult.getArtifact().getFile() ).append( '\n' );
2649                         }
2650                         
2651                         if ( getLog().isDebugEnabled() )
2652                         {
2653                             getLog().debug( sb.toString() );
2654                         }
2655 
2656                     }
2657                     catch ( DependencyResolverException e )
2658                     {
2659                         throw new MavenReportException( e.getMessage(), e );
2660                     }
2661                 }
2662             }
2663         }
2664 
2665         for ( Artifact a : compileArtifactMap.values() )
2666         {
2667             classpathElements.add( a.getFile() );
2668         }
2669 
2670         if ( additionalDependencies != null )
2671         {
2672             for ( Dependency dependency : additionalDependencies )
2673             {
2674                 Artifact artifact = resolveDependency( dependency );
2675                 getLog().debug( "add additional artifact with path " + artifact.getFile() );
2676                 classpathElements.add( artifact.getFile() );
2677             }
2678         }
2679 
2680         return classpathElements;
2681     }
2682 
2683     protected ScopeFilter getDependencyScopeFilter()
2684     {
2685         return ScopeFilter.including( Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_SYSTEM );
2686     }
2687 
2688     /**
2689      * @param dependency {@link Dependency}
2690      * @return {@link Artifact}
2691      * @throws MavenReportException when artifact could not be resolved
2692      */
2693     public Artifact resolveDependency( Dependency dependency )
2694         throws MavenReportException
2695     {
2696         DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
2697         coordinate.setGroupId( dependency.getGroupId() );
2698         coordinate.setArtifactId( dependency.getArtifactId() );
2699         coordinate.setVersion( dependency.getVersion() );
2700         coordinate.setClassifier( dependency.getClassifier() );
2701         coordinate.setExtension( artifactHandlerManager.getArtifactHandler( dependency.getType() ).getExtension() );
2702 
2703         try
2704         {
2705             return artifactResolver.resolveArtifact( session.getProjectBuildingRequest(), coordinate ).getArtifact();
2706         }
2707         catch ( ArtifactResolverException e )
2708         {
2709             throw new MavenReportException( "artifact resolver problem - " + e.getMessage(), e );
2710         }
2711     }
2712 
2713 
2714     //TODO remove the part with ToolchainManager lookup once we depend on
2715     //3.0.9 (have it as prerequisite). Define as regular component field then.
2716     protected final Toolchain getToolchain()
2717     {
2718         Toolchain tc = null;
2719         
2720         if ( jdkToolchain != null )
2721         {
2722             // Maven 3.3.1 has plugin execution scoped Toolchain Support
2723             try
2724             {
2725                 Method getToolchainsMethod =
2726                     toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, String.class,
2727                                                            Map.class );
2728 
2729                 @SuppressWarnings( "unchecked" )
2730                 List<Toolchain> tcs =
2731                     (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk",
2732                                                                   jdkToolchain );
2733 
2734                 if ( tcs != null && tcs.size() > 0 )
2735                 {
2736                     tc = tcs.get( 0 );
2737                 }
2738             }
2739             catch ( NoSuchMethodException e )
2740             {
2741                 // ignore
2742             }
2743             catch ( SecurityException e )
2744             {
2745                 // ignore
2746             }
2747             catch ( IllegalAccessException e )
2748             {
2749                 // ignore
2750             }
2751             catch ( IllegalArgumentException e )
2752             {
2753                 // ignore
2754             }
2755             catch ( InvocationTargetException e )
2756             {
2757                 // ignore
2758             }
2759         }
2760         
2761         if ( tc == null )
2762         {
2763             tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
2764         }
2765         
2766         return tc;
2767     }
2768 
2769     /**
2770      * Method to put the artifacts in the hashmap.
2771      *
2772      * @param compileArtifactMap the hashmap that will contain the artifacts
2773      * @param artifactList       the list of artifacts that will be put in the map
2774      * @throws MavenReportException if any
2775      */
2776     private void populateCompileArtifactMap( Map<String, Artifact> compileArtifactMap,
2777                                              Collection<Artifact> artifactList )
2778         throws MavenReportException
2779     {
2780         if ( artifactList == null )
2781         {
2782             return;
2783         }
2784 
2785         for ( Artifact newArtifact : artifactList )
2786         {
2787             File file = newArtifact.getFile();
2788 
2789             if ( file == null )
2790             {
2791                 throw new MavenReportException(
2792                     "Error in plugin descriptor - " + "dependency was not resolved for artifact: "
2793                         + newArtifact.getGroupId() + ":" + newArtifact.getArtifactId() + ":"
2794                         + newArtifact.getVersion() );
2795             }
2796 
2797             if ( compileArtifactMap.get( newArtifact.getDependencyConflictId() ) != null )
2798             {
2799                 Artifact oldArtifact = compileArtifactMap.get( newArtifact.getDependencyConflictId() );
2800 
2801                 ArtifactVersion oldVersion = new DefaultArtifactVersion( oldArtifact.getVersion() );
2802                 ArtifactVersion newVersion = new DefaultArtifactVersion( newArtifact.getVersion() );
2803                 if ( newVersion.compareTo( oldVersion ) > 0 )
2804                 {
2805                     compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
2806                 }
2807             }
2808             else
2809             {
2810                 compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
2811             }
2812         }
2813     }
2814 
2815     /**
2816      * Method that sets the bottom text that will be displayed on the bottom of the
2817      * javadocs.
2818      *
2819      * @return a String that contains the text that will be displayed at the bottom of the javadoc
2820      */
2821     private String getBottomText()
2822     {
2823         int currentYear = Calendar.getInstance().get( Calendar.YEAR );
2824         String year = String.valueOf( currentYear );
2825 
2826         String inceptionYear = project.getInceptionYear();
2827 
2828         String theBottom = StringUtils.replace( this.bottom, "{currentYear}", year );
2829 
2830         if ( inceptionYear != null )
2831         {
2832             if ( inceptionYear.equals( year ) )
2833             {
2834                 theBottom = StringUtils.replace( theBottom, "{inceptionYear}&#x2013;", "" );
2835             }
2836             else
2837             {
2838                 theBottom = StringUtils.replace( theBottom, "{inceptionYear}", inceptionYear );
2839             }
2840         }
2841         else
2842         {
2843             theBottom = StringUtils.replace( theBottom, "{inceptionYear}&#x2013;", "" );
2844         }
2845 
2846         if ( project.getOrganization() == null )
2847         {
2848             theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
2849         }
2850         else
2851         {
2852             if ( StringUtils.isNotEmpty( project.getOrganization().getName() ) )
2853             {
2854                 if ( StringUtils.isNotEmpty( project.getOrganization().getUrl() ) )
2855                 {
2856                     theBottom = StringUtils.replace( theBottom, "{organizationName}",
2857                                                      "<a href=\"" + project.getOrganization().getUrl() + "\">"
2858                                                          + project.getOrganization().getName() + "</a>" );
2859                 }
2860                 else
2861                 {
2862                     theBottom =
2863                         StringUtils.replace( theBottom, "{organizationName}", project.getOrganization().getName() );
2864                 }
2865             }
2866             else
2867             {
2868                 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
2869             }
2870         }
2871 
2872         return theBottom;
2873     }
2874 
2875     /**
2876      * Method to get the stylesheet path file to be used by the Javadoc Tool.
2877      * <br/>
2878      * If the {@link #stylesheetfile} is empty, return the file as String definded by {@link #stylesheet} value.
2879      * <br/>
2880      * If the {@link #stylesheetfile} is defined, return the file as String.
2881      * <br/>
2882      * Note: since 2.6, the {@link #stylesheetfile} could be a path from a resource in the project source
2883      * directories (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
2884      * or from a resource in the Javadoc plugin dependencies.
2885      *
2886      * @param javadocOutputDirectory the output directory
2887      * @return the stylesheet file absolute path as String.
2888      * @see #getResource(List, String)
2889      */
2890     private String getStylesheetFile( final File javadocOutputDirectory )
2891     {
2892         if ( StringUtils.isEmpty( stylesheetfile ) )
2893         {
2894             if ( "java".equalsIgnoreCase( stylesheet ) )
2895             {
2896                 // use the default Javadoc tool stylesheet
2897                 return null;
2898             }
2899 
2900             // maven, see #copyDefaultStylesheet(File)
2901             return new File( javadocOutputDirectory, DEFAULT_CSS_NAME ).getAbsolutePath();
2902         }
2903 
2904         if ( new File( stylesheetfile ).exists() )
2905         {
2906             return new File( stylesheetfile ).getAbsolutePath();
2907         }
2908 
2909         return getResource( new File( javadocOutputDirectory, DEFAULT_CSS_NAME ), stylesheetfile );
2910     }
2911 
2912     /**
2913      * Method to get the help file to be used by the Javadoc Tool.
2914      * <br/>
2915      * Since 2.6, the {@link #helpfile} could be a path from a resource in the project source
2916      * directories (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
2917      * or from a resource in the Javadoc plugin dependencies.
2918      *
2919      * @param javadocOutputDirectory the output directory.
2920      * @return the help file absolute path as String.
2921      * @see #getResource(File, String)
2922      * @since 2.6
2923      */
2924     private String getHelpFile( final File javadocOutputDirectory )
2925     {
2926         if ( StringUtils.isEmpty( helpfile ) )
2927         {
2928             return null;
2929         }
2930 
2931         if ( new File( helpfile ).exists() )
2932         {
2933             return new File( helpfile ).getAbsolutePath();
2934         }
2935 
2936         return getResource( new File( javadocOutputDirectory, "help-doc.html" ), helpfile );
2937     }
2938 
2939     /**
2940      * Method to get the access level for the classes and members to be shown in the generated javadoc.
2941      * If the specified access level is not public, protected, package or private, the access level
2942      * is set to protected.
2943      *
2944      * @return the access level
2945      */
2946     private String getAccessLevel()
2947     {
2948         String accessLevel;
2949         if ( "public".equalsIgnoreCase( show ) || "protected".equalsIgnoreCase( show ) || "package".equalsIgnoreCase(
2950             show ) || "private".equalsIgnoreCase( show ) )
2951         {
2952             accessLevel = "-" + show;
2953         }
2954         else
2955         {
2956             if ( getLog().isErrorEnabled() )
2957             {
2958                 getLog().error( "Unrecognized access level to show '" + show + "'. Defaulting to protected." );
2959             }
2960             accessLevel = "-protected";
2961         }
2962 
2963         return accessLevel;
2964     }
2965 
2966     /**
2967      * Method to get the path of the bootclass artifacts used in the <code>-bootclasspath</code> option.
2968      *
2969      * @return a string that contains bootclass path, separated by the System pathSeparator string
2970      *         (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
2971      * @throws MavenReportException if any
2972      * @see File#pathSeparator
2973      */
2974     private String getBootclassPath()
2975         throws MavenReportException
2976     {
2977         Set<BootclasspathArtifact> bootclasspathArtifacts = collectBootClasspathArtifacts();
2978 
2979         List<String> bootclassPath = new ArrayList<>();
2980         for ( BootclasspathArtifact aBootclasspathArtifact : bootclasspathArtifacts )
2981         {
2982             if ( ( StringUtils.isNotEmpty( aBootclasspathArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
2983                 aBootclasspathArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
2984                 aBootclasspathArtifact.getVersion() ) ) )
2985             {
2986                 bootclassPath.addAll( getArtifactsAbsolutePath( aBootclasspathArtifact ) );
2987             }
2988         }
2989 
2990         bootclassPath = JavadocUtil.pruneFiles( bootclassPath );
2991 
2992         StringBuilder path = new StringBuilder();
2993         path.append( StringUtils.join( bootclassPath.iterator(), File.pathSeparator ) );
2994 
2995         if ( StringUtils.isNotEmpty( bootclasspath ) )
2996         {
2997             path.append( JavadocUtil.unifyPathSeparator( bootclasspath ) );
2998         }
2999 
3000         return path.toString();
3001     }
3002 
3003     /**
3004      * Method to get the path of the doclet artifacts used in the <code>-docletpath</code> option.
3005      * <p/>
3006      * Either docletArtifact or doclectArtifacts can be defined and used, not both, docletArtifact
3007      * takes precedence over doclectArtifacts. docletPath is always appended to any result path
3008      * definition.
3009      *
3010      * @return a string that contains doclet path, separated by the System pathSeparator string
3011      *         (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
3012      * @throws MavenReportException if any
3013      * @see File#pathSeparator
3014      */
3015     private String getDocletPath()
3016         throws MavenReportException
3017     {
3018         Set<DocletArtifact> docletArtifacts = collectDocletArtifacts();
3019         List<String> pathParts = new ArrayList<>();
3020 
3021         for ( DocletArtifact docletArtifact : docletArtifacts )
3022         {
3023             if ( !isDocletArtifactEmpty( docletArtifact ) )
3024             {
3025                 pathParts.addAll( getArtifactsAbsolutePath( docletArtifact ) );
3026             }
3027         }
3028 
3029         if ( !StringUtils.isEmpty( docletPath ) )
3030         {
3031             pathParts.add( JavadocUtil.unifyPathSeparator( docletPath ) );
3032         }
3033 
3034         String path = StringUtils.join( pathParts.iterator(), File.pathSeparator );
3035 
3036         if ( StringUtils.isEmpty( path ) && getLog().isWarnEnabled() )
3037         {
3038             getLog().warn( "No docletpath option was found. Please review <docletpath/> or <docletArtifact/>"
3039                                + " or <doclets/>." );
3040         }
3041 
3042         return path;
3043     }
3044 
3045     /**
3046      * Verify if a doclet artifact is empty or not
3047      *
3048      * @param aDocletArtifact could be null
3049      * @return <code>true</code> if aDocletArtifact or the groupId/artifactId/version of the doclet artifact is null,
3050      *         <code>false</code> otherwise.
3051      */
3052     private boolean isDocletArtifactEmpty( DocletArtifact aDocletArtifact )
3053     {
3054         if ( aDocletArtifact == null )
3055         {
3056             return true;
3057         }
3058 
3059         return StringUtils.isEmpty( aDocletArtifact.getGroupId() ) && StringUtils.isEmpty(
3060             aDocletArtifact.getArtifactId() ) && StringUtils.isEmpty( aDocletArtifact.getVersion() );
3061     }
3062 
3063     /**
3064      * Method to get the path of the taglet artifacts used in the <code>-tagletpath</code> option.
3065      *
3066      * @return a string that contains taglet path, separated by the System pathSeparator string
3067      *         (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
3068      * @throws MavenReportException if any
3069      * @see File#pathSeparator
3070      */
3071     private String getTagletPath()
3072         throws MavenReportException
3073     {
3074         Set<TagletArtifact> tArtifacts = collectTagletArtifacts();
3075         Collection<String> pathParts = new ArrayList<>();
3076 
3077         for ( TagletArtifact tagletArtifact : tArtifacts )
3078         {
3079             if ( ( tagletArtifact != null ) && ( StringUtils.isNotEmpty( tagletArtifact.getGroupId() ) )
3080                 && ( StringUtils.isNotEmpty( tagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3081                 tagletArtifact.getVersion() ) ) )
3082             {
3083                 pathParts.addAll( getArtifactsAbsolutePath( tagletArtifact ) );
3084             }
3085         }
3086 
3087         Set<Taglet> taglets = collectTaglets();
3088         for ( Taglet taglet : taglets )
3089         {
3090             if ( taglet == null )
3091             {
3092                 continue;
3093             }
3094 
3095             if ( ( taglet.getTagletArtifact() != null ) && ( StringUtils.isNotEmpty(
3096                 taglet.getTagletArtifact().getGroupId() ) ) && ( StringUtils.isNotEmpty(
3097                 taglet.getTagletArtifact().getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3098                 taglet.getTagletArtifact().getVersion() ) ) )
3099             {
3100                 pathParts.addAll( getArtifactsAbsolutePath( taglet.getTagletArtifact() ) );
3101 
3102                 pathParts = JavadocUtil.pruneFiles( pathParts );
3103             }
3104             else if ( StringUtils.isNotEmpty( taglet.getTagletpath() ) )
3105             {
3106                 pathParts.add( taglet.getTagletpath() );
3107 
3108                 pathParts = JavadocUtil.pruneDirs( project, pathParts );
3109             }
3110         }
3111 
3112         StringBuilder path = new StringBuilder();
3113         path.append( StringUtils.join( pathParts.iterator(), File.pathSeparator ) );
3114 
3115         if ( StringUtils.isNotEmpty( tagletpath ) )
3116         {
3117             path.append( JavadocUtil.unifyPathSeparator( tagletpath ) );
3118         }
3119 
3120         return path.toString();
3121     }
3122 
3123     private Set<String> collectLinks()
3124         throws MavenReportException
3125     {
3126         Set<String> links = new LinkedHashSet<>();
3127 
3128         if ( includeDependencySources )
3129         {
3130             try
3131             {
3132                 resolveDependencyBundles();
3133             }
3134             catch ( IOException e )
3135             {
3136                 throw new MavenReportException(
3137                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3138             }
3139 
3140             if ( isNotEmpty( dependencyJavadocBundles ) )
3141             {
3142                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3143                 {
3144                     JavadocOptions options = bundle.getOptions();
3145                     if ( options != null && isNotEmpty( options.getLinks() ) )
3146                     {
3147                         links.addAll( options.getLinks() );
3148                     }
3149                 }
3150             }
3151         }
3152 
3153         if ( isNotEmpty( this.links ) )
3154         {
3155             links.addAll( this.links );
3156         }
3157 
3158         links.addAll( getDependenciesLinks() );
3159 
3160         return followLinks( links );
3161     }
3162 
3163     private Set<Group> collectGroups()
3164         throws MavenReportException
3165     {
3166         Set<Group> groups = new LinkedHashSet<>();
3167 
3168         if ( includeDependencySources )
3169         {
3170             try
3171             {
3172                 resolveDependencyBundles();
3173             }
3174             catch ( IOException e )
3175             {
3176                 throw new MavenReportException(
3177                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3178             }
3179 
3180             if ( isNotEmpty( dependencyJavadocBundles ) )
3181             {
3182                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3183                 {
3184                     JavadocOptions options = bundle.getOptions();
3185                     if ( options != null && isNotEmpty( options.getGroups() ) )
3186                     {
3187                         groups.addAll( options.getGroups() );
3188                     }
3189                 }
3190             }
3191         }
3192 
3193         if ( this.groups != null && this.groups.length > 0 )
3194         {
3195             groups.addAll( Arrays.asList( this.groups ) );
3196         }
3197 
3198         return groups;
3199     }
3200 
3201     private Set<ResourcesArtifact> collectResourcesArtifacts()
3202         throws MavenReportException
3203     {
3204         Set<ResourcesArtifact> result = new LinkedHashSet<>();
3205 
3206         if ( includeDependencySources )
3207         {
3208             try
3209             {
3210                 resolveDependencyBundles();
3211             }
3212             catch ( IOException e )
3213             {
3214                 throw new MavenReportException(
3215                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3216             }
3217 
3218             if ( isNotEmpty( dependencyJavadocBundles ) )
3219             {
3220                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3221                 {
3222                     JavadocOptions options = bundle.getOptions();
3223                     if ( options != null && isNotEmpty( options.getResourcesArtifacts() ) )
3224                     {
3225                         result.addAll( options.getResourcesArtifacts() );
3226                     }
3227                 }
3228             }
3229         }
3230 
3231         if ( this.resourcesArtifacts != null && this.resourcesArtifacts.length > 0 )
3232         {
3233             result.addAll( Arrays.asList( this.resourcesArtifacts ) );
3234         }
3235 
3236         return result;
3237     }
3238 
3239     private Set<BootclasspathArtifact> collectBootClasspathArtifacts()
3240         throws MavenReportException
3241     {
3242         Set<BootclasspathArtifact> result = new LinkedHashSet<>();
3243 
3244         if ( includeDependencySources )
3245         {
3246             try
3247             {
3248                 resolveDependencyBundles();
3249             }
3250             catch ( IOException e )
3251             {
3252                 throw new MavenReportException(
3253                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3254             }
3255 
3256             if ( isNotEmpty( dependencyJavadocBundles ) )
3257             {
3258                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3259                 {
3260                     JavadocOptions options = bundle.getOptions();
3261                     if ( options != null && isNotEmpty( options.getBootclasspathArtifacts() ) )
3262                     {
3263                         result.addAll( options.getBootclasspathArtifacts() );
3264                     }
3265                 }
3266             }
3267         }
3268 
3269         if ( this.bootclasspathArtifacts != null && this.bootclasspathArtifacts.length > 0 )
3270         {
3271             result.addAll( Arrays.asList( this.bootclasspathArtifacts ) );
3272         }
3273 
3274         return result;
3275     }
3276 
3277     private Set<OfflineLink> collectOfflineLinks()
3278         throws MavenReportException
3279     {
3280         Set<OfflineLink> result = new LinkedHashSet<>();
3281 
3282         OfflineLink javaApiLink = getDefaultJavadocApiLink();
3283         if ( javaApiLink != null )
3284         {
3285             result.add( javaApiLink );
3286         }
3287 
3288         if ( includeDependencySources )
3289         {
3290             try
3291             {
3292                 resolveDependencyBundles();
3293             }
3294             catch ( IOException e )
3295             {
3296                 throw new MavenReportException(
3297                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3298             }
3299 
3300             if ( isNotEmpty( dependencyJavadocBundles ) )
3301             {
3302                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3303                 {
3304                     JavadocOptions options = bundle.getOptions();
3305                     if ( options != null && isNotEmpty( options.getOfflineLinks() ) )
3306                     {
3307                         result.addAll( options.getOfflineLinks() );
3308                     }
3309                 }
3310             }
3311         }
3312 
3313         if ( this.offlineLinks != null && this.offlineLinks.length > 0 )
3314         {
3315             result.addAll( Arrays.asList( this.offlineLinks ) );
3316         }
3317 
3318         return result;
3319     }
3320 
3321     private Set<Tag> collectTags()
3322         throws MavenReportException
3323     {
3324         Set<Tag> tags = new LinkedHashSet<>();
3325 
3326         if ( includeDependencySources )
3327         {
3328             try
3329             {
3330                 resolveDependencyBundles();
3331             }
3332             catch ( IOException e )
3333             {
3334                 throw new MavenReportException(
3335                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3336             }
3337 
3338             if ( isNotEmpty( dependencyJavadocBundles ) )
3339             {
3340                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3341                 {
3342                     JavadocOptions options = bundle.getOptions();
3343                     if ( options != null && isNotEmpty( options.getTags() ) )
3344                     {
3345                         tags.addAll( options.getTags() );
3346                     }
3347                 }
3348             }
3349         }
3350 
3351         if ( this.tags != null && this.tags.length > 0 )
3352         {
3353             tags.addAll( Arrays.asList( this.tags ) );
3354         }
3355 
3356         return tags;
3357     }
3358 
3359     private Set<TagletArtifact> collectTagletArtifacts()
3360         throws MavenReportException
3361     {
3362         Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
3363 
3364         if ( includeDependencySources )
3365         {
3366             try
3367             {
3368                 resolveDependencyBundles();
3369             }
3370             catch ( IOException e )
3371             {
3372                 throw new MavenReportException(
3373                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3374             }
3375 
3376             if ( isNotEmpty( dependencyJavadocBundles ) )
3377             {
3378                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3379                 {
3380                     JavadocOptions options = bundle.getOptions();
3381                     if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
3382                     {
3383                         tArtifacts.addAll( options.getTagletArtifacts() );
3384                     }
3385                 }
3386             }
3387         }
3388 
3389         if ( tagletArtifact != null )
3390         {
3391             tArtifacts.add( tagletArtifact );
3392         }
3393 
3394         if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
3395         {
3396             tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
3397         }
3398 
3399         return tArtifacts;
3400     }
3401 
3402     private Set<DocletArtifact> collectDocletArtifacts()
3403         throws MavenReportException
3404     {
3405         Set<DocletArtifact> dArtifacts = new LinkedHashSet<>();
3406 
3407         if ( includeDependencySources )
3408         {
3409             try
3410             {
3411                 resolveDependencyBundles();
3412             }
3413             catch ( IOException e )
3414             {
3415                 throw new MavenReportException(
3416                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3417             }
3418 
3419             if ( isNotEmpty( dependencyJavadocBundles ) )
3420             {
3421                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3422                 {
3423                     JavadocOptions options = bundle.getOptions();
3424                     if ( options != null && isNotEmpty( options.getDocletArtifacts() ) )
3425                     {
3426                         dArtifacts.addAll( options.getDocletArtifacts() );
3427                     }
3428                 }
3429             }
3430         }
3431 
3432         if ( docletArtifact != null )
3433         {
3434             dArtifacts.add( docletArtifact );
3435         }
3436 
3437         if ( docletArtifacts != null && docletArtifacts.length > 0 )
3438         {
3439             dArtifacts.addAll( Arrays.asList( docletArtifacts ) );
3440         }
3441 
3442         return dArtifacts;
3443     }
3444 
3445     private Set<Taglet> collectTaglets()
3446         throws MavenReportException
3447     {
3448         Set<Taglet> result = new LinkedHashSet<>();
3449 
3450         if ( includeDependencySources )
3451         {
3452             try
3453             {
3454                 resolveDependencyBundles();
3455             }
3456             catch ( IOException e )
3457             {
3458                 throw new MavenReportException(
3459                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3460             }
3461 
3462             if ( isNotEmpty( dependencyJavadocBundles ) )
3463             {
3464                 for ( JavadocBundle bundle : dependencyJavadocBundles )
3465                 {
3466                     JavadocOptions options = bundle.getOptions();
3467                     if ( options != null && isNotEmpty( options.getTaglets() ) )
3468                     {
3469                         result.addAll( options.getTaglets() );
3470                     }
3471                 }
3472             }
3473         }
3474 
3475         if ( taglets != null && taglets.length > 0 )
3476         {
3477             result.addAll( Arrays.asList( taglets ) );
3478         }
3479 
3480         return result;
3481     }
3482 
3483     /**
3484      * Return the Javadoc artifact path and its transitive dependencies path from the local repository
3485      *
3486      * @param javadocArtifact not null
3487      * @return a list of locale artifacts absolute path
3488      * @throws MavenReportException if any
3489      */
3490     private List<String> getArtifactsAbsolutePath( JavadocPathArtifact javadocArtifact )
3491         throws MavenReportException
3492     {
3493         if ( ( StringUtils.isEmpty( javadocArtifact.getGroupId() ) ) && ( StringUtils.isEmpty(
3494             javadocArtifact.getArtifactId() ) ) && ( StringUtils.isEmpty( javadocArtifact.getVersion() ) ) )
3495         {
3496             return Collections.emptyList();
3497         }
3498 
3499         List<String> path = new ArrayList<>();
3500 
3501         try
3502         {
3503             Artifact artifact = createAndResolveArtifact( javadocArtifact );
3504             path.add( artifact.getFile().getAbsolutePath() );
3505 
3506             DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate();
3507             coordinate.setGroupId( javadocArtifact.getGroupId() );
3508             coordinate.setArtifactId( javadocArtifact.getArtifactId() );
3509             coordinate.setVersion( javadocArtifact.getVersion() );
3510 
3511             Iterable<ArtifactResult> deps =
3512                 dependencyResolver.resolveDependencies( session.getProjectBuildingRequest(), coordinate,
3513                                                         ScopeFilter.including( "compile", "provided" ) );
3514             for ( ArtifactResult a : deps )
3515             {
3516                 path.add( a.getArtifact().getFile().getAbsolutePath() );
3517             }
3518 
3519             return path;
3520         }
3521         catch ( ArtifactResolverException e )
3522         {
3523             throw new MavenReportException( "Unable to resolve artifact:" + javadocArtifact, e );
3524         }
3525         catch ( DependencyResolverException e )
3526         {
3527             throw new MavenReportException( "Unable to resolve dependencies for:" + javadocArtifact, e );
3528         }
3529     }
3530 
3531     /**
3532      * creates an {@link Artifact} representing the configured {@link JavadocPathArtifact} and resolves it.
3533      *
3534      * @param javadocArtifact the {@link JavadocPathArtifact} to resolve
3535      * @return a resolved {@link Artifact}
3536      * @throws ArtifactResolverException
3537      */
3538     private Artifact createAndResolveArtifact( JavadocPathArtifact javadocArtifact )
3539         throws ArtifactResolverException
3540     {
3541         DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
3542         coordinate.setGroupId( javadocArtifact.getGroupId() );
3543         coordinate.setArtifactId( javadocArtifact.getArtifactId() );
3544         coordinate.setVersion( javadocArtifact.getVersion() );
3545 
3546         return artifactResolver.resolveArtifact( session.getProjectBuildingRequest(), coordinate ).getArtifact();
3547     }
3548 
3549     /**
3550      * Method that adds/sets the java memory parameters in the command line execution.
3551      *
3552      * @param cmd    the command line execution object where the argument will be added
3553      * @param arg    the argument parameter name
3554      * @param memory the JVM memory value to be set
3555      * @see JavadocUtil#parseJavadocMemory(String)
3556      */
3557     private void addMemoryArg( Commandline cmd, String arg, String memory )
3558     {
3559         if ( StringUtils.isNotEmpty( memory ) )
3560         {
3561             try
3562             {
3563                 cmd.createArg().setValue( "-J" + arg + JavadocUtil.parseJavadocMemory( memory ) );
3564             }
3565             catch ( IllegalArgumentException e )
3566             {
3567                 if ( getLog().isErrorEnabled() )
3568                 {
3569                     getLog().error( "Malformed memory pattern for '" + arg + memory + "'. Ignore this option." );
3570                 }
3571             }
3572         }
3573     }
3574 
3575     /**
3576      * Method that adds/sets the javadoc proxy parameters in the command line execution.
3577      *
3578      * @param cmd the command line execution object where the argument will be added
3579      */
3580     private void addProxyArg( Commandline cmd )
3581     {
3582         if ( settings == null || settings.getActiveProxy() == null )
3583         {
3584             return;
3585         }
3586 
3587         Proxy activeProxy = settings.getActiveProxy();
3588         String protocol = StringUtils.isNotEmpty( activeProxy.getProtocol() ) ? activeProxy.getProtocol() + "." : "";
3589 
3590         if ( StringUtils.isNotEmpty( activeProxy.getHost() ) )
3591         {
3592             cmd.createArg().setValue( "-J-D" + protocol + "proxySet=true" );
3593             cmd.createArg().setValue( "-J-D" + protocol + "proxyHost=" + activeProxy.getHost() );
3594 
3595             if ( activeProxy.getPort() > 0 )
3596             {
3597                 cmd.createArg().setValue( "-J-D" + protocol + "proxyPort=" + activeProxy.getPort() );
3598             }
3599 
3600             if ( StringUtils.isNotEmpty( activeProxy.getNonProxyHosts() ) )
3601             {
3602                 cmd.createArg().setValue(
3603                     "-J-D" + protocol + "nonProxyHosts=\"" + activeProxy.getNonProxyHosts() + "\"" );
3604             }
3605 
3606             if ( StringUtils.isNotEmpty( activeProxy.getUsername() ) )
3607             {
3608                 cmd.createArg().setValue( "-J-Dhttp.proxyUser=\"" + activeProxy.getUsername() + "\"" );
3609 
3610                 if ( StringUtils.isNotEmpty( activeProxy.getPassword() ) )
3611                 {
3612                     cmd.createArg().setValue( "-J-Dhttp.proxyPassword=\"" + activeProxy.getPassword() + "\"" );
3613                 }
3614             }
3615         }
3616     }
3617 
3618     /**
3619      * Get the path of the Javadoc tool executable depending the user entry or try to find it depending the OS
3620      * or the <code>java.home</code> system property or the <code>JAVA_HOME</code> environment variable.
3621      *
3622      * @return the path of the Javadoc tool
3623      * @throws IOException if not found
3624      */
3625     private String getJavadocExecutable()
3626         throws IOException
3627     {
3628         Toolchain tc = getToolchain();
3629 
3630         if ( tc != null )
3631         {
3632             getLog().info( "Toolchain in maven-javadoc-plugin: " + tc );
3633             if ( javadocExecutable != null )
3634             {
3635                 getLog().warn( "Toolchains are ignored, 'javadocExecutable' parameter is set to " + javadocExecutable );
3636             }
3637             else
3638             {
3639                 javadocExecutable = tc.findTool( "javadoc" );
3640             }
3641         }
3642 
3643         String javadocCommand = "javadoc" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
3644 
3645         File javadocExe;
3646 
3647         // ----------------------------------------------------------------------
3648         // The javadoc executable is defined by the user
3649         // ----------------------------------------------------------------------
3650         if ( StringUtils.isNotEmpty( javadocExecutable ) )
3651         {
3652             javadocExe = new File( javadocExecutable );
3653 
3654             if ( javadocExe.isDirectory() )
3655             {
3656                 javadocExe = new File( javadocExe, javadocCommand );
3657             }
3658 
3659             if ( SystemUtils.IS_OS_WINDOWS && javadocExe.getName().indexOf( '.' ) < 0 )
3660             {
3661                 javadocExe = new File( javadocExe.getPath() + ".exe" );
3662             }
3663 
3664             if ( !javadocExe.isFile() )
3665             {
3666                 throw new IOException( "The javadoc executable '" + javadocExe
3667                     + "' doesn't exist or is not a file. Verify the <javadocExecutable/> parameter." );
3668             }
3669 
3670             return javadocExe.getAbsolutePath();
3671         }
3672 
3673         // ----------------------------------------------------------------------
3674         // Try to find javadocExe from System.getProperty( "java.home" )
3675         // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME
3676         // should be in the JDK_HOME
3677         // ----------------------------------------------------------------------
3678         // For IBM's JDK 1.2
3679         if ( SystemUtils.IS_OS_AIX )
3680         {
3681             javadocExe =
3682                 new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", javadocCommand );
3683         }
3684         // For Apple's JDK 1.6.x (and older?) on Mac OSX
3685         // CHECKSTYLE_OFF: MagicNumber
3686         else if ( SystemUtils.IS_OS_MAC_OSX && !JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast( "1.7" ) )
3687         // CHECKSTYLE_ON: MagicNumber
3688         {
3689             javadocExe = new File( SystemUtils.getJavaHome() + File.separator + "bin", javadocCommand );
3690         }
3691         else
3692         {
3693             javadocExe =
3694                 new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", javadocCommand );
3695         }
3696 
3697         // ----------------------------------------------------------------------
3698         // Try to find javadocExe from JAVA_HOME environment variable
3699         // ----------------------------------------------------------------------
3700         if ( !javadocExe.exists() || !javadocExe.isFile() )
3701         {
3702             Properties env = CommandLineUtils.getSystemEnvVars();
3703             String javaHome = env.getProperty( "JAVA_HOME" );
3704             if ( StringUtils.isEmpty( javaHome ) )
3705             {
3706                 throw new IOException( "The environment variable JAVA_HOME is not correctly set." );
3707             }
3708             if ( ( !new File( javaHome ).getCanonicalFile().exists() )
3709                 || ( new File( javaHome ).getCanonicalFile().isFile() ) )
3710             {
3711                 throw new IOException( "The environment variable JAVA_HOME=" + javaHome
3712                     + " doesn't exist or is not a valid directory." );
3713             }
3714 
3715             javadocExe = new File( javaHome + File.separator + "bin", javadocCommand );
3716         }
3717 
3718         if ( !javadocExe.getCanonicalFile().exists() || !javadocExe.getCanonicalFile().isFile() )
3719         {
3720             throw new IOException( "The javadoc executable '" + javadocExe
3721                 + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." );
3722         }
3723 
3724         return javadocExe.getAbsolutePath();
3725     }
3726 
3727     /**
3728      * Set a new value for <code>javadocRuntimeVersion</code>
3729      *
3730      * @param jExecutable not null
3731      * @throws MavenReportException if not found
3732      * @see JavadocUtil#getJavadocVersion(File)
3733      */
3734     private void setFJavadocVersion( File jExecutable )
3735         throws MavenReportException
3736     {
3737         JavaVersion jVersion;
3738         try
3739         {
3740             jVersion = JavadocUtil.getJavadocVersion( jExecutable );
3741         }
3742         catch ( IOException e )
3743         {
3744             if ( getLog().isWarnEnabled() )
3745             {
3746                 getLog().warn( "Unable to find the javadoc version: " + e.getMessage() );
3747                 getLog().warn( "Using the Java version instead of, i.e. " + JAVA_VERSION );
3748             }
3749             jVersion = JAVA_VERSION;
3750         }
3751         catch ( CommandLineException e )
3752         {
3753             if ( getLog().isWarnEnabled() )
3754             {
3755                 getLog().warn( "Unable to find the javadoc version: " + e.getMessage() );
3756                 getLog().warn( "Using the Java version instead of, i.e. " + JAVA_VERSION );
3757             }
3758             jVersion = JAVA_VERSION;
3759         }
3760         catch ( IllegalArgumentException e )
3761         {
3762             if ( getLog().isWarnEnabled() )
3763             {
3764                 getLog().warn( "Unable to find the javadoc version: " + e.getMessage() );
3765                 getLog().warn( "Using the Java version instead of, i.e. " + JAVA_VERSION );
3766             }
3767             jVersion = JAVA_VERSION;
3768         }
3769 
3770         if ( StringUtils.isNotEmpty( javadocVersion ) )
3771         {
3772             try
3773             {
3774                 javadocRuntimeVersion = JavaVersion.parse( javadocVersion );
3775             }
3776             catch ( NumberFormatException e )
3777             {
3778                 throw new MavenReportException( "Unable to parse javadoc version: " + e.getMessage(), e );
3779             }
3780 
3781             if ( javadocRuntimeVersion.compareTo( jVersion ) != 0 && getLog().isWarnEnabled() )
3782             {
3783                 getLog().warn( "Are you sure about the <javadocVersion/> parameter? It seems to be " + jVersion );
3784             }
3785         }
3786         else
3787         {
3788             javadocRuntimeVersion = jVersion;
3789         }
3790     }
3791 
3792     /**
3793      * Is the Javadoc version at least the requested version.
3794      *
3795      * @param requiredVersion the required version, for example 1.5f
3796      * @return <code>true</code> if the javadoc version is equal or greater than the
3797      *         required version
3798      */
3799     private boolean isJavaDocVersionAtLeast( JavaVersion requiredVersion )
3800     {
3801         return JAVA_VERSION.compareTo( requiredVersion ) >= 0;
3802     }
3803 
3804     /**
3805      * Convenience method to add an argument to the <code>command line</code>
3806      * conditionally based on the given flag.
3807      *
3808      * @param arguments a list of arguments, not null
3809      * @param b         the flag which controls if the argument is added or not.
3810      * @param value     the argument value to be added.
3811      */
3812     private void addArgIf( List<String> arguments, boolean b, String value )
3813     {
3814         if ( b )
3815         {
3816             arguments.add( value );
3817         }
3818     }
3819 
3820     /**
3821      * Convenience method to add an argument to the <code>command line</code>
3822      * regarding the requested Java version.
3823      *
3824      * @param arguments           a list of arguments, not null
3825      * @param b                   the flag which controls if the argument is added or not.
3826      * @param value               the argument value to be added.
3827      * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
3828      * @see #addArgIf(java.util.List, boolean, String)
3829      * @see #isJavaDocVersionAtLeast(float)
3830      */
3831     private void addArgIf( List<String> arguments, boolean b, String value, JavaVersion requiredJavaVersion )
3832     {
3833         if ( b )
3834         {
3835             if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
3836             {
3837                 addArgIf( arguments, true, value );
3838             }
3839             else
3840             {
3841                 if ( getLog().isWarnEnabled() )
3842                 {
3843                     getLog().warn( value + " option is not supported on Java version < " + requiredJavaVersion
3844                                        + ". Ignore this option." );
3845                 }
3846             }
3847         }
3848     }
3849 
3850     /**
3851      * Convenience method to add an argument to the <code>command line</code>
3852      * if the the value is not null or empty.
3853      * <p/>
3854      * Moreover, the value could be comma separated.
3855      *
3856      * @param arguments a list of arguments, not null
3857      * @param key       the argument name.
3858      * @param value     the argument value to be added.
3859      * @see #addArgIfNotEmpty(java.util.List, String, String, boolean)
3860      */
3861     private void addArgIfNotEmpty( List<String> arguments, String key, String value )
3862     {
3863         addArgIfNotEmpty( arguments, key, value, false );
3864     }
3865 
3866     /**
3867      * Convenience method to add an argument to the <code>command line</code>
3868      * if the the value is not null or empty.
3869      * <p/>
3870      * Moreover, the value could be comma separated.
3871      *
3872      * @param arguments           a list of arguments, not null
3873      * @param key                 the argument name.
3874      * @param value               the argument value to be added.
3875      * @param repeatKey           repeat or not the key in the command line
3876      * @param splitValue          if <code>true</code> given value will be tokenized by comma
3877      * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
3878      * @see #addArgIfNotEmpty(List, String, String, boolean, boolean)
3879      * @see #isJavaDocVersionAtLeast(float)
3880      */
3881     private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
3882                                    boolean splitValue, JavaVersion requiredJavaVersion )
3883     {
3884         if ( StringUtils.isNotEmpty( value ) )
3885         {
3886             if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
3887             {
3888                 addArgIfNotEmpty( arguments, key, value, repeatKey, splitValue );
3889             }
3890             else
3891             {
3892                 if ( getLog().isWarnEnabled() )
3893                 {
3894                     getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion
3895                                        + ". Ignore this option." );
3896                 }
3897             }
3898         }
3899     }
3900 
3901     /**
3902      * Convenience method to add an argument to the <code>command line</code>
3903      * if the the value is not null or empty.
3904      * <p/>
3905      * Moreover, the value could be comma separated.
3906      *
3907      * @param arguments  a list of arguments, not null
3908      * @param key        the argument name.
3909      * @param value      the argument value to be added.
3910      * @param repeatKey  repeat or not the key in the command line
3911      * @param splitValue if <code>true</code> given value will be tokenized by comma
3912      */
3913     private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
3914                                    boolean splitValue )
3915     {
3916         if ( StringUtils.isNotEmpty( value ) )
3917         {
3918             if ( StringUtils.isNotEmpty( key ) )
3919             {
3920                 arguments.add( key );
3921             }
3922 
3923             if ( splitValue )
3924             {
3925                 StringTokenizer token = new StringTokenizer( value, "," );
3926                 while ( token.hasMoreTokens() )
3927                 {
3928                     String current = token.nextToken().trim();
3929 
3930                     if ( StringUtils.isNotEmpty( current ) )
3931                     {
3932                         arguments.add( current );
3933 
3934                         if ( token.hasMoreTokens() && repeatKey )
3935                         {
3936                             arguments.add( key );
3937                         }
3938                     }
3939                 }
3940             }
3941             else
3942             {
3943                 arguments.add( value );
3944             }
3945         }
3946     }
3947 
3948     /**
3949      * Convenience method to add an argument to the <code>command line</code>
3950      * if the the value is not null or empty.
3951      * <p/>
3952      * Moreover, the value could be comma separated.
3953      *
3954      * @param arguments a list of arguments, not null
3955      * @param key       the argument name.
3956      * @param value     the argument value to be added.
3957      * @param repeatKey repeat or not the key in the command line
3958      */
3959     private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey )
3960     {
3961         addArgIfNotEmpty( arguments, key, value, repeatKey, true );
3962     }
3963 
3964     /**
3965      * Convenience method to add an argument to the <code>command line</code>
3966      * regarding the requested Java version.
3967      *
3968      * @param arguments           a list of arguments, not null
3969      * @param key                 the argument name.
3970      * @param value               the argument value to be added.
3971      * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
3972      * @see #addArgIfNotEmpty(java.util.List, String, String, float, boolean)
3973      */
3974     private void addArgIfNotEmpty( List<String> arguments, String key, String value,
3975                                    JavaVersion requiredJavaVersion )
3976     {
3977         addArgIfNotEmpty( arguments, key, value, requiredJavaVersion, false );
3978     }
3979 
3980     /**
3981      * Convenience method to add an argument to the <code>command line</code>
3982      * regarding the requested Java version.
3983      *
3984      * @param arguments           a list of arguments, not null
3985      * @param key                 the argument name.
3986      * @param value               the argument value to be added.
3987      * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
3988      * @param repeatKey           repeat or not the key in the command line
3989      * @see #addArgIfNotEmpty(java.util.List, String, String)
3990      * @see #isJavaDocVersionAtLeast(float)
3991      */
3992     private void addArgIfNotEmpty( List<String> arguments, String key, String value, JavaVersion requiredJavaVersion,
3993                                    boolean repeatKey )
3994     {
3995         if ( StringUtils.isNotEmpty( value ) )
3996         {
3997             if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
3998             {
3999                 addArgIfNotEmpty( arguments, key, value, repeatKey );
4000             }
4001             else
4002             {
4003                 if ( getLog().isWarnEnabled() )
4004                 {
4005                     getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion );
4006                 }
4007             }
4008         }
4009     }
4010 
4011     /**
4012      * Convenience method to process {@link #offlineLinks} values as individual <code>-linkoffline</code>
4013      * javadoc options.
4014      * <br/>
4015      * If {@link #detectOfflineLinks}, try to add javadoc apidocs according Maven conventions for all modules given
4016      * in the project.
4017      *
4018      * @param arguments a list of arguments, not null
4019      * @throws MavenReportException if any
4020      * @see #offlineLinks
4021      * @see #getModulesLinks()
4022      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#package-list">package-list spec</a>
4023      */
4024     private void addLinkofflineArguments( List<String> arguments )
4025         throws MavenReportException
4026     {
4027         Set<OfflineLink> offlineLinksList = collectOfflineLinks();
4028 
4029         offlineLinksList.addAll( getModulesLinks() );
4030 
4031         for ( OfflineLink offlineLink : offlineLinksList )
4032         {
4033             String url = offlineLink.getUrl();
4034             if ( StringUtils.isEmpty( url ) )
4035             {
4036                 continue;
4037             }
4038             url = cleanUrl( url );
4039 
4040             String location = offlineLink.getLocation();
4041             if ( StringUtils.isEmpty( location ) )
4042             {
4043                 continue;
4044             }
4045             if ( isValidJavadocLink( location, false ) )
4046             {
4047                 addArgIfNotEmpty( arguments, "-linkoffline",
4048                                   JavadocUtil.quotedPathArgument( url ) + " " + JavadocUtil.quotedPathArgument(
4049                                       location ), true );
4050             }
4051         }
4052     }
4053 
4054     /**
4055      * Convenience method to process {@link #links} values as individual <code>-link</code> javadoc options.
4056      * If {@link #detectLinks}, try to add javadoc apidocs according Maven conventions for all dependencies given
4057      * in the project.
4058      * <br/>
4059      * According the Javadoc documentation, all defined link should have <code>${link}/package-list</code> fetchable.
4060      * <br/>
4061      * <b>Note</b>: when a link is not fetchable:
4062      * <ul>
4063      * <li>Javadoc 1.4 and less throw an exception</li>
4064      * <li>Javadoc 1.5 and more display a warning</li>
4065      * </ul>
4066      *
4067      * @param arguments a list of arguments, not null
4068      * @throws MavenReportException
4069      * @see #detectLinks
4070      * @see #getDependenciesLinks()
4071      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#package-list">package-list spec</a>
4072      */
4073     private void addLinkArguments( List<String> arguments )
4074         throws MavenReportException
4075     {
4076         Set<String> links = collectLinks();
4077 
4078         for ( String link : links )
4079         {
4080             if ( StringUtils.isEmpty( link ) )
4081             {
4082                 continue;
4083             }
4084 
4085             while ( link.endsWith( "/" ) )
4086             {
4087                 link = link.substring( 0, link.lastIndexOf( "/" ) );
4088             }
4089 
4090             addArgIfNotEmpty( arguments, "-link", JavadocUtil.quotedPathArgument( link ), true, false );
4091         }
4092     }
4093 
4094     /**
4095      * Coppy all resources to the output directory
4096      *
4097      * @param javadocOutputDirectory not null
4098      * @throws MavenReportException if any
4099      * @see #copyDefaultStylesheet(File)
4100      * @see #copyJavadocResources(File)
4101      * @see #copyAdditionalJavadocResources(File)
4102      */
4103     private void copyAllResources( File javadocOutputDirectory )
4104         throws MavenReportException
4105     {
4106         // ----------------------------------------------------------------------
4107         // Copy default resources
4108         // ----------------------------------------------------------------------
4109 
4110         try
4111         {
4112             copyDefaultStylesheet( javadocOutputDirectory );
4113         }
4114         catch ( IOException e )
4115         {
4116             throw new MavenReportException( "Unable to copy default stylesheet: " + e.getMessage(), e );
4117         }
4118 
4119         // ----------------------------------------------------------------------
4120         // Copy javadoc resources
4121         // ----------------------------------------------------------------------
4122 
4123         if ( docfilessubdirs )
4124         {
4125             /*
4126              * Workaround since -docfilessubdirs doesn't seem to be used correctly by the javadoc tool
4127              * (see other note about -sourcepath). Take care of the -excludedocfilessubdir option.
4128              */
4129             try
4130             {
4131                 copyJavadocResources( javadocOutputDirectory );
4132             }
4133             catch ( IOException e )
4134             {
4135                 throw new MavenReportException( "Unable to copy javadoc resources: " + e.getMessage(), e );
4136             }
4137         }
4138 
4139         // ----------------------------------------------------------------------
4140         // Copy additional javadoc resources in artifacts
4141         // ----------------------------------------------------------------------
4142 
4143         copyAdditionalJavadocResources( javadocOutputDirectory );
4144     }
4145 
4146     /**
4147      * Copies the {@link #DEFAULT_CSS_NAME} css file from the current class
4148      * loader to the <code>outputDirectory</code> only if {@link #stylesheetfile} is empty and
4149      * {@link #stylesheet} is equals to <code>maven</code>.
4150      *
4151      * @param anOutputDirectory the output directory
4152      * @throws java.io.IOException if any
4153      * @see #DEFAULT_CSS_NAME
4154      * @see JavadocUtil#copyResource(java.net.URL, java.io.File)
4155      */
4156     private void copyDefaultStylesheet( File anOutputDirectory )
4157         throws IOException
4158     {
4159         if ( StringUtils.isNotEmpty( stylesheetfile ) )
4160         {
4161             return;
4162         }
4163 
4164         if ( !stylesheet.equalsIgnoreCase( "maven" ) )
4165         {
4166             return;
4167         }
4168 
4169         URL url = getClass().getClassLoader().getResource( RESOURCE_CSS_DIR + "/" + DEFAULT_CSS_NAME );
4170         File outFile = new File( anOutputDirectory, DEFAULT_CSS_NAME );
4171         JavadocUtil.copyResource( url, outFile );
4172     }
4173 
4174     /**
4175      * Method that copy all <code>doc-files</code> directories from <code>javadocDirectory</code> of
4176      * the current project or of the projects in the reactor to the <code>outputDirectory</code>.
4177      *
4178      * @param anOutputDirectory the output directory
4179      * @throws java.io.IOException if any
4180      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.2.html#docfiles">Reference
4181      *      Guide, Copies new "doc-files" directory for holding images and examples</a>
4182      * @see #docfilessubdirs
4183      */
4184     private void copyJavadocResources( File anOutputDirectory )
4185         throws IOException
4186     {
4187         if ( anOutputDirectory == null || !anOutputDirectory.exists() )
4188         {
4189             throw new IOException( "The outputDirectory " + anOutputDirectory + " doesn't exists." );
4190         }
4191 
4192         if ( includeDependencySources )
4193         {
4194             resolveDependencyBundles();
4195             if ( isNotEmpty( dependencyJavadocBundles ) )
4196             {
4197                 for ( JavadocBundle bundle : dependencyJavadocBundles )
4198                 {
4199                     File dir = bundle.getResourcesDirectory();
4200                     JavadocOptions options = bundle.getOptions();
4201                     if ( dir != null && dir.isDirectory() )
4202                     {
4203                         JavadocUtil.copyJavadocResources( anOutputDirectory, dir, options == null
4204                             ? null
4205                             : options.getExcludedDocfilesSubdirs() );
4206                     }
4207                 }
4208             }
4209         }
4210 
4211         if ( getJavadocDirectory() != null )
4212         {
4213             JavadocUtil.copyJavadocResources( anOutputDirectory, getJavadocDirectory(), excludedocfilessubdir );
4214         }
4215 
4216         if ( isAggregator() && project.isExecutionRoot() )
4217         {
4218             for ( MavenProject subProject : reactorProjects )
4219             {
4220                 if ( subProject != project && getJavadocDirectory() != null )
4221                 {
4222                     String javadocDirRelative =
4223                         PathUtils.toRelative( project.getBasedir(), getJavadocDirectory().getAbsolutePath() );
4224                     File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
4225                     JavadocUtil.copyJavadocResources( anOutputDirectory, javadocDir, excludedocfilessubdir );
4226                 }
4227             }
4228         }
4229     }
4230 
4231     private synchronized void resolveDependencyBundles()
4232         throws IOException
4233     {
4234         if ( dependencyJavadocBundles == null )
4235         {
4236             dependencyJavadocBundles =
4237                 resourceResolver.resolveDependencyJavadocBundles( getDependencySourceResolverConfig() );
4238             if ( dependencyJavadocBundles == null )
4239             {
4240                 dependencyJavadocBundles = new ArrayList<>();
4241             }
4242         }
4243     }
4244 
4245     /**
4246      * Method that copy additional Javadoc resources from given artifacts.
4247      *
4248      * @param anOutputDirectory the output directory
4249      * @throws MavenReportException if any
4250      * @see #resourcesArtifacts
4251      */
4252     private void copyAdditionalJavadocResources( File anOutputDirectory )
4253         throws MavenReportException
4254     {
4255         Set<ResourcesArtifact> resourcesArtifacts = collectResourcesArtifacts();
4256         if ( isEmpty( resourcesArtifacts ) )
4257         {
4258             return;
4259         }
4260 
4261         UnArchiver unArchiver;
4262         try
4263         {
4264             unArchiver = archiverManager.getUnArchiver( "jar" );
4265         }
4266         catch ( NoSuchArchiverException e )
4267         {
4268             throw new MavenReportException(
4269                 "Unable to extract resources artifact. " + "No archiver for 'jar' available.", e );
4270         }
4271 
4272         for ( ResourcesArtifact item : resourcesArtifacts )
4273         {
4274             Artifact artifact;
4275             try
4276             {
4277                 artifact = createAndResolveArtifact( item );
4278             }
4279             catch ( ArtifactResolverException e )
4280             {
4281                 throw new MavenReportException( "Unable to resolve artifact:" + item, e );
4282             }
4283 
4284             unArchiver.setSourceFile( artifact.getFile() );
4285             unArchiver.setDestDirectory( anOutputDirectory );
4286             // remove the META-INF directory from resource artifact
4287             IncludeExcludeFileSelector[] selectors =
4288                 new IncludeExcludeFileSelector[]{ new IncludeExcludeFileSelector() };
4289             selectors[0].setExcludes( new String[]{ "META-INF/**" } );
4290             unArchiver.setFileSelectors( selectors );
4291 
4292             getLog().info( "Extracting contents of resources artifact: " + artifact.getArtifactId() );
4293             try
4294             {
4295                 unArchiver.extract();
4296             }
4297             catch ( ArchiverException e )
4298             {
4299                 throw new MavenReportException(
4300                     "Extraction of resources failed. Artifact that failed was: " + artifact.getArtifactId(), e );
4301             }
4302         }
4303     }
4304 
4305     /**
4306      * @param sourcePaths could be null
4307      * @param files       not null
4308      * @return the list of package names for files in the sourcePaths
4309      */
4310     private List<String> getPackageNames( Collection<String> sourcePaths, List<String> files )
4311     {
4312         return getPackageNamesOrFilesWithUnnamedPackages( sourcePaths, files, true );
4313     }
4314 
4315     /**
4316      * @param sourcePaths could be null
4317      * @param files       not null
4318      * @return a list files with unnamed package names for files in the sourecPaths
4319      */
4320     private List<String> getFilesWithUnnamedPackages( Collection<String> sourcePaths, List<String> files )
4321     {
4322         return getPackageNamesOrFilesWithUnnamedPackages( sourcePaths, files, false );
4323     }
4324 
4325     /**
4326      * @param sourcePaths     not null, containing absolute and relative paths
4327      * @param files           not null, containing list of quoted files
4328      * @param onlyPackageName boolean for only package name
4329      * @return a list of package names or files with unnamed package names, depending the value of the unnamed flag
4330      * @see #getFiles(List)
4331      * @see #getSourcePaths()
4332      */
4333     private List<String> getPackageNamesOrFilesWithUnnamedPackages( Collection<String> sourcePaths, List<String> files,
4334                                                                     boolean onlyPackageName )
4335     {
4336         List<String> returnList = new ArrayList<>();
4337 
4338         if ( !StringUtils.isEmpty( sourcepath ) )
4339         {
4340             return returnList;
4341         }
4342 
4343         for ( String currentFile : files )
4344         {
4345             currentFile = currentFile.replace( '\\', '/' );
4346 
4347             for ( String currentSourcePath : sourcePaths )
4348             {
4349                 currentSourcePath = currentSourcePath.replace( '\\', '/' );
4350 
4351                 if ( !currentSourcePath.endsWith( "/" ) )
4352                 {
4353                     currentSourcePath += "/";
4354                 }
4355 
4356                 if ( currentFile.contains( currentSourcePath ) )
4357                 {
4358                     String packagename = currentFile.substring( currentSourcePath.length() + 1 );
4359 
4360                     /*
4361                      * Remove the miscellaneous files
4362                      * http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
4363                      */
4364                     if ( packagename.contains( "doc-files" ) )
4365                     {
4366                         continue;
4367                     }
4368 
4369                     if ( onlyPackageName && packagename.lastIndexOf( "/" ) != -1 )
4370                     {
4371                         packagename = packagename.substring( 0, packagename.lastIndexOf( "/" ) );
4372                         packagename = packagename.replace( '/', '.' );
4373 
4374                         if ( !returnList.contains( packagename ) )
4375                         {
4376                             returnList.add( packagename );
4377                         }
4378                     }
4379                     if ( !onlyPackageName && packagename.lastIndexOf( "/" ) == -1 )
4380                     {
4381                         returnList.add( currentFile );
4382                     }
4383                 }
4384             }
4385         }
4386 
4387         return returnList;
4388     }
4389 
4390     /**
4391      * Generate an <code>options</code> file for all options and arguments and add the <code>@options</code> in the
4392      * command line.
4393      *
4394      * @param cmd                    not null
4395      * @param arguments              not null
4396      * @param javadocOutputDirectory not null
4397      * @throws MavenReportException if any
4398      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#argumentfiles">
4399      *      Reference Guide, Command line argument files</a>
4400      * @see #OPTIONS_FILE_NAME
4401      */
4402     private void addCommandLineOptions( Commandline cmd, List<String> arguments, File javadocOutputDirectory )
4403         throws MavenReportException
4404     {
4405         File optionsFile = new File( javadocOutputDirectory, OPTIONS_FILE_NAME );
4406 
4407         StringBuilder options = new StringBuilder();
4408         options.append( StringUtils.join( arguments.toArray( new String[arguments.size()] ),
4409                                           SystemUtils.LINE_SEPARATOR ) );
4410 
4411         try
4412         {
4413             FileUtils.fileWrite( optionsFile.getAbsolutePath(), null /* platform encoding */, options.toString() );
4414         }
4415         catch ( IOException e )
4416         {
4417             throw new MavenReportException(
4418                 "Unable to write '" + optionsFile.getName() + "' temporary file for command execution", e );
4419         }
4420 
4421         cmd.createArg().setValue( "@" + OPTIONS_FILE_NAME );
4422     }
4423 
4424     /**
4425      * Generate a file called <code>argfile</code> (or <code>files</code>, depending the JDK) to hold files and add
4426      * the <code>@argfile</code> (or <code>@file</code>, depending the JDK) in the command line.
4427      *
4428      * @param cmd                    not null
4429      * @param javadocOutputDirectory not null
4430      * @param files                  not null
4431      * @throws MavenReportException if any
4432      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#argumentfiles">
4433      *      Reference Guide, Command line argument files
4434      *      </a>
4435      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#runningjavadoc">
4436      *      What s New in Javadoc 1.4
4437      *      </a>
4438      * @see #isJavaDocVersionAtLeast(float)
4439      * @see #ARGFILE_FILE_NAME
4440      * @see #FILES_FILE_NAME
4441      */
4442     private void addCommandLineArgFile( Commandline cmd, File javadocOutputDirectory, List<String> files )
4443         throws MavenReportException
4444     {
4445         File argfileFile;
4446         if ( JAVA_VERSION.compareTo( SINCE_JAVADOC_1_4 ) >= 0 )
4447         {
4448             argfileFile = new File( javadocOutputDirectory, ARGFILE_FILE_NAME );
4449             cmd.createArg().setValue( "@" + ARGFILE_FILE_NAME );
4450         }
4451         else
4452         {
4453             argfileFile = new File( javadocOutputDirectory, FILES_FILE_NAME );
4454             cmd.createArg().setValue( "@" + FILES_FILE_NAME );
4455         }
4456 
4457         try
4458         {
4459             FileUtils.fileWrite( argfileFile.getAbsolutePath(), null /* platform encoding */,
4460                                  StringUtils.join( files.iterator(), SystemUtils.LINE_SEPARATOR ) );
4461         }
4462         catch ( IOException e )
4463         {
4464             throw new MavenReportException(
4465                 "Unable to write '" + argfileFile.getName() + "' temporary file for command execution", e );
4466         }
4467     }
4468 
4469     /**
4470      * Generate a file called <code>packages</code> to hold all package names and add the <code>@packages</code> in
4471      * the command line.
4472      *
4473      * @param cmd                    not null
4474      * @param javadocOutputDirectory not null
4475      * @param packageNames           not null
4476      * @throws MavenReportException if any
4477      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#argumentfiles">
4478      *      Reference Guide, Command line argument files</a>
4479      * @see #PACKAGES_FILE_NAME
4480      */
4481     private void addCommandLinePackages( Commandline cmd, File javadocOutputDirectory, List<String> packageNames )
4482         throws MavenReportException
4483     {
4484         File packagesFile = new File( javadocOutputDirectory, PACKAGES_FILE_NAME );
4485 
4486         try
4487         {
4488             FileUtils.fileWrite( packagesFile.getAbsolutePath(), null /* platform encoding */,
4489                                  StringUtils.join( packageNames.iterator(), SystemUtils.LINE_SEPARATOR ) );
4490         }
4491         catch ( IOException e )
4492         {
4493             throw new MavenReportException(
4494                 "Unable to write '" + packagesFile.getName() + "' temporary file for command execution", e );
4495         }
4496 
4497         cmd.createArg().setValue( "@" + PACKAGES_FILE_NAME );
4498     }
4499 
4500     /**
4501      * Checks for the validity of the Javadoc options used by the user.
4502      *
4503      * @throws MavenReportException if error
4504      */
4505     private void validateJavadocOptions()
4506         throws MavenReportException
4507     {
4508         // encoding
4509         if ( StringUtils.isNotEmpty( getEncoding() ) && !JavadocUtil.validateEncoding( getEncoding() ) )
4510         {
4511             throw new MavenReportException( "Unsupported option <encoding/> '" + getEncoding() + "'" );
4512         }
4513 
4514         // locale
4515         if ( StringUtils.isNotEmpty( this.locale ) )
4516         {
4517             StringTokenizer tokenizer = new StringTokenizer( this.locale, "_" );
4518             final int maxTokens = 3;
4519             if ( tokenizer.countTokens() > maxTokens )
4520             {
4521                 throw new MavenReportException(
4522                     "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
4523             }
4524 
4525             Locale localeObject = null;
4526             if ( tokenizer.hasMoreTokens() )
4527             {
4528                 String language = tokenizer.nextToken().toLowerCase( Locale.ENGLISH );
4529                 if ( !Arrays.asList( Locale.getISOLanguages() ).contains( language ) )
4530                 {
4531                     throw new MavenReportException(
4532                         "Unsupported language '" + language + "' in option <locale/> '" + this.locale + "'" );
4533                 }
4534                 localeObject = new Locale( language );
4535 
4536                 if ( tokenizer.hasMoreTokens() )
4537                 {
4538                     String country = tokenizer.nextToken().toUpperCase( Locale.ENGLISH );
4539                     if ( !Arrays.asList( Locale.getISOCountries() ).contains( country ) )
4540                     {
4541                         throw new MavenReportException(
4542                             "Unsupported country '" + country + "' in option <locale/> '" + this.locale + "'" );
4543                     }
4544                     localeObject = new Locale( language, country );
4545 
4546                     if ( tokenizer.hasMoreTokens() )
4547                     {
4548                         String variant = tokenizer.nextToken();
4549                         localeObject = new Locale( language, country, variant );
4550                     }
4551                 }
4552             }
4553 
4554             if ( localeObject == null )
4555             {
4556                 throw new MavenReportException(
4557                     "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
4558             }
4559 
4560             this.locale = localeObject.toString();
4561             final List<Locale> availableLocalesList = Arrays.asList( Locale.getAvailableLocales() );
4562             if ( StringUtils.isNotEmpty( localeObject.getVariant() ) && !availableLocalesList.contains( localeObject ) )
4563             {
4564                 StringBuilder sb = new StringBuilder();
4565                 sb.append( "Unsupported option <locale/> with variant '" ).append( this.locale );
4566                 sb.append( "'" );
4567 
4568                 localeObject = new Locale( localeObject.getLanguage(), localeObject.getCountry() );
4569                 this.locale = localeObject.toString();
4570 
4571                 sb.append( ", trying to use <locale/> without variant, i.e. '" ).append( this.locale ).append( "'" );
4572                 if ( getLog().isWarnEnabled() )
4573                 {
4574                     getLog().warn( sb.toString() );
4575                 }
4576             }
4577 
4578             if ( !availableLocalesList.contains( localeObject ) )
4579             {
4580                 throw new MavenReportException( "Unsupported option <locale/> '" + this.locale + "'" );
4581             }
4582         }
4583     }
4584 
4585     /**
4586      * Checks for the validity of the Standard Doclet options.
4587      * <br/>
4588      * For example, throw an exception if &lt;nohelp/&gt; and &lt;helpfile/&gt; options are used together.
4589      *
4590      * @throws MavenReportException if error or conflict found
4591      */
4592     private void validateStandardDocletOptions()
4593         throws MavenReportException
4594     {
4595         // docencoding
4596         if ( StringUtils.isNotEmpty( getDocencoding() ) && !JavadocUtil.validateEncoding( getDocencoding() ) )
4597         {
4598             throw new MavenReportException( "Unsupported option <docencoding/> '" + getDocencoding() + "'" );
4599         }
4600 
4601         // charset
4602         if ( StringUtils.isNotEmpty( getCharset() ) && !JavadocUtil.validateEncoding( getCharset() ) )
4603         {
4604             throw new MavenReportException( "Unsupported option <charset/> '" + getCharset() + "'" );
4605         }
4606 
4607         // helpfile
4608         if ( StringUtils.isNotEmpty( helpfile ) && nohelp )
4609         {
4610             throw new MavenReportException( "Option <nohelp/> conflicts with <helpfile/>" );
4611         }
4612 
4613         // overview
4614         if ( ( getOverview() != null ) && nooverview )
4615         {
4616             throw new MavenReportException( "Option <nooverview/> conflicts with <overview/>" );
4617         }
4618 
4619         // index
4620         if ( splitindex && noindex )
4621         {
4622             throw new MavenReportException( "Option <noindex/> conflicts with <splitindex/>" );
4623         }
4624 
4625         // stylesheet
4626         if ( StringUtils.isNotEmpty( stylesheet ) && !( stylesheet.equalsIgnoreCase( "maven" )
4627             || stylesheet.equalsIgnoreCase( "java" ) ) )
4628         {
4629             throw new MavenReportException( "Option <stylesheet/> supports only \"maven\" or \"java\" value." );
4630         }
4631 
4632         // default java api links
4633         if ( javaApiLinks == null || javaApiLinks.size() == 0 )
4634         {
4635             javaApiLinks = DEFAULT_JAVA_API_LINKS;
4636         }
4637     }
4638 
4639     /**
4640      * Add Standard Javadoc Options.
4641      * <br/>
4642      * The <a href="package-summary.html#Standard_Javadoc_Options">package documentation</a> details the
4643      * Standard Javadoc Options wrapped by this Plugin.
4644      *
4645      * @param javadocOutputDirectory not null
4646      * @param arguments   not null
4647      * @param sourcePaths not null
4648      * @throws MavenReportException if any
4649      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadocoptions">http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadocoptions</a>
4650      */
4651     private void addJavadocOptions( File javadocOutputDirectory,
4652                                     List<String> arguments,
4653                                     Map<String, Collection<String>> allSourcePaths )
4654         throws MavenReportException
4655     {
4656         Collection<String> sourcePaths = collect( allSourcePaths.values() );
4657         
4658         validateJavadocOptions();
4659 
4660         // see com.sun.tools.javadoc.Start#parseAndExecute(String argv[])
4661         addArgIfNotEmpty( arguments, "-locale", JavadocUtil.quotedArgument( this.locale ) );
4662 
4663         // all options in alphabetical order
4664 
4665         if ( old && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_4 ) )
4666         {
4667             if ( getLog().isWarnEnabled() )
4668             {
4669                 getLog().warn( "Javadoc 1.4+ doesn't support the -1.1 switch anymore. Ignore this option." );
4670             }
4671         }
4672         else
4673         {
4674             addArgIf( arguments, old, "-1.1" );
4675         }
4676 
4677         addArgIfNotEmpty( arguments, "-bootclasspath", JavadocUtil.quotedPathArgument( getBootclassPath() ) );
4678 
4679         if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
4680         {
4681             addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_5 );
4682         }
4683 
4684         List<String> roots = getProjectSourceRoots( getProject() );
4685         
4686         File mainDescriptor = findMainDescriptor( roots );
4687 
4688         final LocationManager locationManager = new LocationManager();
4689 
4690         if ( mainDescriptor != null && !isTest() )
4691         {
4692             ResolvePathsRequest<File> request =
4693                 ResolvePathsRequest.withFiles( getPathElements() ).setMainModuleDescriptor( mainDescriptor );
4694             try
4695             {
4696                 ResolvePathsResult<File> result = locationManager.resolvePaths( request );
4697 
4698                 String classpath = StringUtils.join( result.getClasspathElements().iterator(), File.pathSeparator );
4699                 addArgIfNotEmpty( arguments, "--class-path", JavadocUtil.quotedPathArgument( classpath ) );
4700 
4701                 Set<File> modulePathElements = new HashSet<>( result.getModulepathElements().keySet() )  ;
4702                 if ( allSourcePaths.size() > 1 )
4703                 {
4704                     // Probably required due to bug in javadoc (Java 9+)   
4705                     modulePathElements.addAll( getProjectBuildOutputDirs( getProject() ) );
4706                 }
4707                 
4708                 String modulepath =
4709                     StringUtils.join( modulePathElements.iterator(), File.pathSeparator );
4710                 addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ) );
4711             }
4712             catch ( IOException e )
4713             {
4714                 throw new MavenReportException( e.getMessage(), e );
4715             }
4716         }
4717         else
4718         {
4719             String classpath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
4720             addArgIfNotEmpty( arguments, "-classpath", JavadocUtil.quotedPathArgument( classpath ) );
4721         }
4722 
4723         Collection<String> reactorKeys = new HashSet<>( session.getProjects().size() );
4724         for ( MavenProject reactorProject : session.getProjects() )
4725         {
4726             reactorKeys.add( ArtifactUtils.versionlessKey( reactorProject.getGroupId(),
4727                                                            reactorProject.getArtifactId() ) );
4728         }
4729         
4730         Path moduleSourceDir = null;
4731         if ( allSourcePaths.size() > 1 )
4732         {
4733             for ( Map.Entry<String, Collection<String>> projectSourcepaths : allSourcePaths.entrySet() )
4734             {
4735                 if ( reactorKeys.contains( projectSourcepaths.getKey() ) )
4736                 {
4737                     File moduleDescriptor = findMainDescriptor( projectSourcepaths.getValue() );
4738                     if ( moduleDescriptor != null )
4739                     {
4740                         moduleSourceDir = javadocOutputDirectory.toPath().resolve( "src" );
4741                         try
4742                         {
4743                             moduleSourceDir = Files.createDirectories( moduleSourceDir );
4744                             ResolvePathsRequest<File> request =
4745                                 ResolvePathsRequest.withFiles( Collections.<File>emptyList() )
4746                                                    .setMainModuleDescriptor( moduleDescriptor );
4747                             
4748                             String moduleName =
4749                                 locationManager.resolvePaths( request ).getMainModuleDescriptor().name();
4750                             
4751                             addArgIfNotEmpty( arguments, "--patch-module", moduleName + '='
4752                                 + JavadocUtil.quotedPathArgument( getSourcePath( projectSourcepaths.getValue() ) ) );
4753                             
4754                             Files.createDirectory( moduleSourceDir.resolve( moduleName ) );
4755                         }
4756                         catch ( IOException e )
4757                         {
4758                             throw new MavenReportException( e.getMessage() );
4759                         }
4760                     }
4761                     else
4762                     {
4763                         // todo
4764                         getLog().error( "no module descriptor for " + projectSourcepaths.getKey() );
4765                     }
4766                 }
4767                 else
4768                 {
4769                     // todo
4770                     getLog().error( "no reactor project: " + projectSourcepaths.getKey() );
4771 
4772                 }
4773             }
4774         }
4775         
4776         if ( StringUtils.isNotEmpty( doclet ) )
4777         {
4778             addArgIfNotEmpty( arguments, "-doclet", JavadocUtil.quotedArgument( doclet ) );
4779             addArgIfNotEmpty( arguments, "-docletpath", JavadocUtil.quotedPathArgument( getDocletPath() ) );
4780         }
4781 
4782         if ( StringUtils.isEmpty( encoding ) )
4783         {
4784             getLog().warn(
4785                 "Source files encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
4786                     + ", i.e. build is platform dependent!" );
4787         }
4788         addArgIfNotEmpty( arguments, "-encoding", JavadocUtil.quotedArgument( getEncoding() ) );
4789 
4790         addArgIfNotEmpty( arguments, "-extdirs",
4791                           JavadocUtil.quotedPathArgument( JavadocUtil.unifyPathSeparator( extdirs ) ) );
4792 
4793         if ( ( getOverview() != null ) && ( getOverview().exists() ) )
4794         {
4795             addArgIfNotEmpty( arguments, "-overview",
4796                               JavadocUtil.quotedPathArgument( getOverview().getAbsolutePath() ) );
4797         }
4798 
4799         arguments.add( getAccessLevel() );
4800 
4801         if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
4802         {
4803             addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_5 );
4804         }
4805 
4806         addArgIfNotEmpty( arguments, "-source", JavadocUtil.quotedArgument( source ), SINCE_JAVADOC_1_4 );
4807 
4808         if ( ( StringUtils.isEmpty( sourcepath ) ) && ( StringUtils.isNotEmpty( subpackages ) ) )
4809         {
4810             sourcepath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
4811         }
4812         
4813         if ( moduleSourceDir != null )
4814         {
4815             addArgIfNotEmpty( arguments, "--module-source-path",
4816                               JavadocUtil.quotedPathArgument( moduleSourceDir.toString() ) );
4817         }
4818         else
4819         {
4820             addArgIfNotEmpty( arguments, "-sourcepath",
4821                               JavadocUtil.quotedPathArgument( getSourcePath( sourcePaths ) ) );
4822         }
4823 
4824 
4825         if ( StringUtils.isNotEmpty( sourcepath ) && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
4826         {
4827             addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_5 );
4828         }
4829         
4830         // [MJAVADOC-497] must be after sourcepath is recalculated, since getExcludedPackages() depends on it
4831         addArgIfNotEmpty( arguments, "-exclude", getExcludedPackages( sourcePaths ), SINCE_JAVADOC_1_4 );
4832 
4833         addArgIf( arguments, verbose, "-verbose" );
4834 
4835         if ( additionalOptions != null && additionalOptions.length > 0 )
4836         {
4837             for ( String option : additionalOptions )
4838             {
4839                 arguments.add( option );
4840             }
4841         }
4842     }
4843 
4844     private File findMainDescriptor( Collection<String> roots )
4845     {
4846         for ( String root : roots )
4847         {
4848             File descriptorFile = new File( root, "module-info.java" ).getAbsoluteFile();
4849             if ( descriptorFile.exists() )
4850             {
4851                 return descriptorFile;
4852             }
4853         }
4854         return null;
4855     }
4856 
4857     /**
4858      * Add Standard Doclet Options.
4859      * <br/>
4860      * The <a href="package-summary.html#Standard_Doclet_Options">package documentation</a> details the
4861      * Standard Doclet Options wrapped by this Plugin.
4862      *
4863      * @param javadocOutputDirectory not null
4864      * @param arguments              not null
4865      * @throws MavenReportException if any
4866      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#standard">
4867      *      http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#standard</a>
4868      */
4869     private void addStandardDocletOptions( File javadocOutputDirectory, List<String> arguments )
4870         throws MavenReportException
4871     {
4872         validateStandardDocletOptions();
4873 
4874         // all options in alphabetical order
4875 
4876         addArgIf( arguments, author, "-author" );
4877 
4878         addArgIfNotEmpty( arguments, "-bottom", JavadocUtil.quotedArgument( getBottomText() ), false, false );
4879 
4880         if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
4881         {
4882             addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_4 );
4883         }
4884 
4885         addArgIfNotEmpty( arguments, "-charset", JavadocUtil.quotedArgument( getCharset() ) );
4886 
4887         addArgIfNotEmpty( arguments, "-d", JavadocUtil.quotedPathArgument( javadocOutputDirectory.toString() ) );
4888 
4889         addArgIfNotEmpty( arguments, "-docencoding", JavadocUtil.quotedArgument( getDocencoding() ) );
4890 
4891         addArgIf( arguments, docfilessubdirs, "-docfilessubdirs", SINCE_JAVADOC_1_4 );
4892 
4893         addArgIf( arguments, StringUtils.isNotEmpty( doclint ), "-Xdoclint:" + getDoclint(), SINCE_JAVADOC_1_8 );
4894 
4895         addArgIfNotEmpty( arguments, "-doctitle", JavadocUtil.quotedArgument( getDoctitle() ), false, false );
4896 
4897         if ( docfilessubdirs )
4898         {
4899             addArgIfNotEmpty( arguments, "-excludedocfilessubdir",
4900                               JavadocUtil.quotedPathArgument( excludedocfilessubdir ), SINCE_JAVADOC_1_4 );
4901         }
4902 
4903         addArgIfNotEmpty( arguments, "-footer", JavadocUtil.quotedArgument( footer ), false, false );
4904 
4905         addGroups( arguments );
4906 
4907         addArgIfNotEmpty( arguments, "-header", JavadocUtil.quotedArgument( header ), false, false );
4908 
4909         addArgIfNotEmpty( arguments, "-helpfile",
4910                           JavadocUtil.quotedPathArgument( getHelpFile( javadocOutputDirectory ) ) );
4911 
4912         addArgIf( arguments, keywords, "-keywords", SINCE_JAVADOC_1_4_2 );
4913 
4914         if ( !isOffline )
4915         {
4916             addLinkArguments( arguments );
4917         }
4918 
4919         addLinkofflineArguments( arguments );
4920 
4921         addArgIf( arguments, linksource, "-linksource", SINCE_JAVADOC_1_4 );
4922 
4923         if ( sourcetab > 0 )
4924         {
4925             if ( javadocRuntimeVersion == SINCE_JAVADOC_1_4_2 )
4926             {
4927                 addArgIfNotEmpty( arguments, "-linksourcetab", String.valueOf( sourcetab ) );
4928             }
4929             addArgIfNotEmpty( arguments, "-sourcetab", String.valueOf( sourcetab ), SINCE_JAVADOC_1_5 );
4930         }
4931 
4932         addArgIf( arguments, nocomment, "-nocomment", SINCE_JAVADOC_1_4 );
4933 
4934         addArgIf( arguments, nodeprecated, "-nodeprecated" );
4935 
4936         addArgIf( arguments, nodeprecatedlist, "-nodeprecatedlist" );
4937 
4938         addArgIf( arguments, nohelp, "-nohelp" );
4939 
4940         addArgIf( arguments, noindex, "-noindex" );
4941 
4942         addArgIf( arguments, nonavbar, "-nonavbar" );
4943 
4944         addArgIf( arguments, nooverview, "-nooverview" );
4945 
4946         addArgIfNotEmpty( arguments, "-noqualifier", JavadocUtil.quotedArgument( noqualifier ), SINCE_JAVADOC_1_4 );
4947 
4948         addArgIf( arguments, nosince, "-nosince" );
4949 
4950         addArgIf( arguments, notimestamp, "-notimestamp", SINCE_JAVADOC_1_5 );
4951 
4952         addArgIf( arguments, notree, "-notree" );
4953 
4954         addArgIfNotEmpty( arguments, "-packagesheader", JavadocUtil.quotedArgument( packagesheader ),
4955                           SINCE_JAVADOC_1_4_2 );
4956 
4957         if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) ) // Sun bug: 4714350
4958         {
4959             addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_4 );
4960         }
4961 
4962         addArgIf( arguments, serialwarn, "-serialwarn" );
4963 
4964         addArgIf( arguments, splitindex, "-splitindex" );
4965 
4966         addArgIfNotEmpty( arguments, "-stylesheetfile",
4967                           JavadocUtil.quotedPathArgument( getStylesheetFile( javadocOutputDirectory ) ) );
4968 
4969         if ( StringUtils.isNotEmpty( sourcepath ) && !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
4970         {
4971             addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_4 );
4972         }
4973 
4974         addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet ), SINCE_JAVADOC_1_4 );
4975         addTaglets( arguments );
4976         addTagletsFromTagletArtifacts( arguments );
4977         addArgIfNotEmpty( arguments, "-tagletpath", JavadocUtil.quotedPathArgument( getTagletPath() ),
4978                           SINCE_JAVADOC_1_4 );
4979 
4980         addTags( arguments );
4981 
4982         addArgIfNotEmpty( arguments, "-top", JavadocUtil.quotedArgument( top ), false, false, SINCE_JAVADOC_1_6 );
4983 
4984         addArgIf( arguments, use, "-use" );
4985 
4986         addArgIf( arguments, version, "-version" );
4987 
4988         addArgIfNotEmpty( arguments, "-windowtitle", JavadocUtil.quotedArgument( getWindowtitle() ), false, false );
4989     }
4990 
4991     /**
4992      * Add <code>groups</code> parameter to arguments.
4993      *
4994      * @param arguments not null
4995      * @throws MavenReportException
4996      */
4997     private void addGroups( List<String> arguments )
4998         throws MavenReportException
4999     {
5000         Set<Group> groups = collectGroups();
5001         if ( isEmpty( groups ) )
5002         {
5003             return;
5004         }
5005 
5006         for ( Group group : groups )
5007         {
5008             if ( group == null || StringUtils.isEmpty( group.getTitle() ) || StringUtils.isEmpty(
5009                 group.getPackages() ) )
5010             {
5011                 if ( getLog().isWarnEnabled() )
5012                 {
5013                     getLog().warn( "A group option is empty. Ignore this option." );
5014                 }
5015             }
5016             else
5017             {
5018                 String groupTitle = StringUtils.replace( group.getTitle(), ",", "&#44;" );
5019                 addArgIfNotEmpty( arguments, "-group",
5020                                   JavadocUtil.quotedArgument( groupTitle ) + " " + JavadocUtil.quotedArgument(
5021                                       group.getPackages() ), true );
5022             }
5023         }
5024     }
5025 
5026     /**
5027      * Add <code>tags</code> parameter to arguments.
5028      *
5029      * @param arguments not null
5030      * @throws MavenReportException
5031      */
5032     private void addTags( List<String> arguments )
5033         throws MavenReportException
5034     {
5035         Set<Tag> tags = collectTags();
5036 
5037         if ( isEmpty( tags ) )
5038         {
5039             return;
5040         }
5041 
5042         for ( Tag tag : tags )
5043         {
5044             if ( StringUtils.isEmpty( tag.getName() ) )
5045             {
5046                 if ( getLog().isWarnEnabled() )
5047                 {
5048                     getLog().warn( "A tag name is empty. Ignore this option." );
5049                 }
5050             }
5051             else
5052             {
5053                 String value = "\"" + tag.getName();
5054                 if ( StringUtils.isNotEmpty( tag.getPlacement() ) )
5055                 {
5056                     value += ":" + tag.getPlacement();
5057                     if ( StringUtils.isNotEmpty( tag.getHead() ) )
5058                     {
5059                         value += ":" + tag.getHead();
5060                     }
5061                 }
5062                 value += "\"";
5063                 addArgIfNotEmpty( arguments, "-tag", value, SINCE_JAVADOC_1_4 );
5064             }
5065         }
5066     }
5067 
5068     /**
5069      * Add <code>taglets</code> parameter to arguments.
5070      *
5071      * @param arguments not null
5072      */
5073     private void addTaglets( List<String> arguments )
5074     {
5075         if ( taglets == null )
5076         {
5077             return;
5078         }
5079 
5080         for ( Taglet taglet1 : taglets )
5081         {
5082             if ( ( taglet1 == null ) || ( StringUtils.isEmpty( taglet1.getTagletClass() ) ) )
5083             {
5084                 if ( getLog().isWarnEnabled() )
5085                 {
5086                     getLog().warn( "A taglet option is empty. Ignore this option." );
5087                 }
5088             }
5089             else
5090             {
5091                 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet1.getTagletClass() ),
5092                                   SINCE_JAVADOC_1_4 );
5093             }
5094         }
5095     }
5096 
5097     /**
5098      * Auto-detect taglets class name from <code>tagletArtifacts</code> and add them to arguments.
5099      *
5100      * @param arguments not null
5101      * @throws MavenReportException if any
5102      * @see JavadocUtil#getTagletClassNames(File)
5103      */
5104     private void addTagletsFromTagletArtifacts( List<String> arguments )
5105         throws MavenReportException
5106     {
5107         Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
5108         if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
5109         {
5110             tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
5111         }
5112 
5113         if ( includeDependencySources )
5114         {
5115             try
5116             {
5117                 resolveDependencyBundles();
5118             }
5119             catch ( IOException e )
5120             {
5121                 throw new MavenReportException(
5122                     "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
5123             }
5124 
5125             if ( isNotEmpty( dependencyJavadocBundles ) )
5126             {
5127                 for ( JavadocBundle bundle : dependencyJavadocBundles )
5128                 {
5129                     JavadocOptions options = bundle.getOptions();
5130                     if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
5131                     {
5132                         tArtifacts.addAll( options.getTagletArtifacts() );
5133                     }
5134                 }
5135             }
5136         }
5137 
5138         if ( isEmpty( tArtifacts ) )
5139         {
5140             return;
5141         }
5142 
5143         List<String> tagletsPath = new ArrayList<>();
5144 
5145         for ( TagletArtifact aTagletArtifact : tArtifacts )
5146         {
5147             if ( ( StringUtils.isNotEmpty( aTagletArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
5148                 aTagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty( aTagletArtifact.getVersion() ) ) )
5149             {
5150                 Artifact artifact;
5151                 try
5152                 {
5153                     artifact = createAndResolveArtifact( aTagletArtifact );
5154                 }
5155                 catch ( ArtifactResolverException e )
5156                 {
5157                     throw new MavenReportException( "Unable to resolve artifact:" + aTagletArtifact, e );
5158                 }
5159 
5160                 tagletsPath.add( artifact.getFile().getAbsolutePath() );
5161             }
5162         }
5163 
5164         tagletsPath = JavadocUtil.pruneFiles( tagletsPath );
5165 
5166         for ( String tagletJar : tagletsPath )
5167         {
5168             if ( !tagletJar.toLowerCase( Locale.ENGLISH ).endsWith( ".jar" ) )
5169             {
5170                 continue;
5171             }
5172 
5173             List<String> tagletClasses;
5174             try
5175             {
5176                 tagletClasses = JavadocUtil.getTagletClassNames( new File( tagletJar ) );
5177             }
5178             catch ( IOException e )
5179             {
5180                 if ( getLog().isWarnEnabled() )
5181                 {
5182                     getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5183                                        + "'. Try to specify them with <taglets/>." );
5184                 }
5185                 if ( getLog().isDebugEnabled() )
5186                 {
5187                     getLog().debug( "IOException: " + e.getMessage(), e );
5188                 }
5189                 continue;
5190             }
5191             catch ( ClassNotFoundException e )
5192             {
5193                 if ( getLog().isWarnEnabled() )
5194                 {
5195                     getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5196                                        + "'. Try to specify them with <taglets/>." );
5197                 }
5198                 if ( getLog().isDebugEnabled() )
5199                 {
5200                     getLog().debug( "ClassNotFoundException: " + e.getMessage(), e );
5201                 }
5202                 continue;
5203             }
5204             catch ( NoClassDefFoundError e )
5205             {
5206                 if ( getLog().isWarnEnabled() )
5207                 {
5208                     getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5209                                        + "'. Try to specify them with <taglets/>." );
5210                 }
5211                 if ( getLog().isDebugEnabled() )
5212                 {
5213                     getLog().debug( "NoClassDefFoundError: " + e.getMessage(), e );
5214                 }
5215                 continue;
5216             }
5217 
5218             if ( tagletClasses != null && !tagletClasses.isEmpty() )
5219             {
5220                 for ( String tagletClass : tagletClasses )
5221                 {
5222                     addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( tagletClass ),
5223                                       SINCE_JAVADOC_1_4 );
5224                 }
5225             }
5226         }
5227     }
5228 
5229     /**
5230      * Execute the Javadoc command line
5231      *
5232      * @param cmd                    not null
5233      * @param javadocOutputDirectory not null
5234      * @throws MavenReportException if any errors occur
5235      */
5236     private void executeJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
5237         throws MavenReportException
5238     {
5239         if ( getLog().isDebugEnabled() )
5240         {
5241             // no quoted arguments
5242             getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) );
5243         }
5244 
5245         String cmdLine = null;
5246         if ( debug )
5247         {
5248             cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
5249             cmdLine = JavadocUtil.hideProxyPassword( cmdLine, settings );
5250 
5251             writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
5252         }
5253 
5254         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
5255         CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
5256         try
5257         {
5258             int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
5259 
5260             String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() );
5261 
5262             if ( exitCode != 0 )
5263             {
5264                 if ( cmdLine == null )
5265                 {
5266                     cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
5267                     cmdLine = JavadocUtil.hideProxyPassword( cmdLine, settings );
5268                 }
5269                 writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
5270 
5271                 if ( StringUtils.isNotEmpty( output ) && StringUtils.isEmpty( err.getOutput() )
5272                     && isJavadocVMInitError( output ) )
5273                 {
5274                     throw new MavenReportException( output + '\n' + '\n' + JavadocUtil.ERROR_INIT_VM + '\n'
5275                         + "Or, try to reduce the Java heap size for the Javadoc goal using "
5276                         + "-Dminmemory=<size> and -Dmaxmemory=<size>." + '\n' + '\n' + "Command line was: " + cmdLine
5277                         + '\n' + '\n' + "Refer to the generated Javadoc files in '" + javadocOutputDirectory
5278                         + "' dir.\n" );
5279                 }
5280 
5281                 if ( StringUtils.isNotEmpty( output ) )
5282                 {
5283                     getLog().info( output );
5284                 }
5285 
5286                 StringBuilder msg = new StringBuilder( "\nExit code: " );
5287                 msg.append( exitCode );
5288                 if ( StringUtils.isNotEmpty( err.getOutput() ) )
5289                 {
5290                     msg.append( " - " ).append( err.getOutput() );
5291                 }
5292                 msg.append( '\n' );
5293                 msg.append( "Command line was: " ).append( cmdLine ).append( '\n' ).append( '\n' );
5294 
5295                 msg.append( "Refer to the generated Javadoc files in '" ).append( javadocOutputDirectory )
5296                 .append( "' dir.\n" );
5297 
5298                 throw new MavenReportException( msg.toString() );
5299             }
5300 
5301             if ( StringUtils.isNotEmpty( output ) )
5302             {
5303                 getLog().info( output );
5304             }
5305         }
5306         catch ( CommandLineException e )
5307         {
5308             throw new MavenReportException( "Unable to execute javadoc command: " + e.getMessage(), e );
5309         }
5310 
5311         // ----------------------------------------------------------------------
5312         // Handle Javadoc warnings
5313         // ----------------------------------------------------------------------
5314 
5315         if ( StringUtils.isNotEmpty( err.getOutput() ) && getLog().isWarnEnabled() )
5316         {
5317             getLog().warn( "Javadoc Warnings" );
5318 
5319             StringTokenizer token = new StringTokenizer( err.getOutput(), "\n" );
5320             while ( token.hasMoreTokens() )
5321             {
5322                 String current = token.nextToken().trim();
5323 
5324                 getLog().warn( current );
5325             }
5326         }
5327 
5328         if ( StringUtils.isNotEmpty( err.getOutput() ) && failOnWarnings )
5329         {
5330             throw new MavenReportException( "Project contains Javadoc Warnings" );
5331         }
5332     }
5333 
5334     /**
5335      * Patches the given Javadoc output directory to work around CVE-2013-1571
5336      * (see http://www.kb.cert.org/vuls/id/225657).
5337      *
5338      * @param javadocOutputDirectory directory to scan for vulnerabilities
5339      * @param outputEncoding         encoding used by the javadoc tool (-docencoding parameter).
5340      *                               If {@code null}, the platform's default encoding is used (like javadoc does).
5341      * @return the number of patched files
5342      */
5343     private int fixFrameInjectionBug( File javadocOutputDirectory, String outputEncoding )
5344         throws IOException
5345     {
5346         final String fixData;
5347         InputStream in = null;
5348         try
5349         {
5350             in = this.getClass().getResourceAsStream( "frame-injection-fix.txt" );
5351             if ( in == null )
5352             {
5353                 throw new FileNotFoundException( "Missing resource 'frame-injection-fix.txt' in classpath." );
5354             }
5355             fixData = StringUtils.unifyLineSeparators( IOUtil.toString( in, "US-ASCII" ) ).trim();
5356             in.close();
5357             in = null;
5358         }
5359         finally
5360         {
5361             IOUtil.close( in );
5362         }
5363 
5364         final DirectoryScanner ds = new DirectoryScanner();
5365         ds.setBasedir( javadocOutputDirectory );
5366         ds.setCaseSensitive( false );
5367         ds.setIncludes( new String[]{ "**/index.html", "**/index.htm", "**/toc.html", "**/toc.htm" } );
5368         ds.addDefaultExcludes();
5369         ds.scan();
5370         int patched = 0;
5371         for ( String f : ds.getIncludedFiles() )
5372         {
5373             final File file = new File( javadocOutputDirectory, f );
5374             // we load the whole file as one String (toc/index files are
5375             // generally small, because they only contain frameset declaration):
5376             final String fileContents = FileUtils.fileRead( file, outputEncoding );
5377             // check if file may be vulnerable because it was not patched with "validURL(url)":
5378             if ( !StringUtils.contains( fileContents, "function validURL(url) {" ) )
5379             {
5380                 // we need to patch the file!
5381                 final String patchedFileContents =
5382                     StringUtils.replaceOnce( fileContents, "function loadFrames() {", fixData );
5383                 if ( !patchedFileContents.equals( fileContents ) )
5384                 {
5385                     FileUtils.fileWrite( file, outputEncoding, patchedFileContents );
5386                     patched++;
5387                 }
5388             }
5389         }
5390         return patched;
5391     }
5392 
5393     /**
5394      * @param outputFile        not nul
5395      * @param inputResourceName a not null resource in <code>src/main/java</code>, <code>src/main/resources</code> or
5396      *                          <code>src/main/javadoc</code> or in the Javadoc plugin dependencies.
5397      * @return the resource file absolute path as String
5398      * @since 2.6
5399      */
5400     private String getResource( File outputFile, String inputResourceName )
5401     {
5402         if ( inputResourceName.startsWith( "/" ) )
5403         {
5404             inputResourceName = inputResourceName.replaceFirst( "//*", "" );
5405         }
5406 
5407         List<String> classPath = new ArrayList<>();
5408         classPath.add( project.getBuild().getSourceDirectory() );
5409 
5410         URL resourceURL = getResource( classPath, inputResourceName );
5411         if ( resourceURL != null )
5412         {
5413             getLog().debug( inputResourceName + " found in the main src directory of the project." );
5414             return FileUtils.toFile( resourceURL ).getAbsolutePath();
5415         }
5416 
5417         classPath.clear();
5418         List<Resource> resources = project.getBuild().getResources();
5419         for ( Resource resource : resources )
5420         {
5421             classPath.add( resource.getDirectory() );
5422         }
5423         resourceURL = getResource( classPath, inputResourceName );
5424         if ( resourceURL != null )
5425         {
5426             getLog().debug( inputResourceName + " found in the main resources directories of the project." );
5427             return FileUtils.toFile( resourceURL ).getAbsolutePath();
5428         }
5429 
5430         if ( javadocDirectory.exists() )
5431         {
5432             classPath.clear();
5433             classPath.add( javadocDirectory.getAbsolutePath() );
5434             resourceURL = getResource( classPath, inputResourceName );
5435             if ( resourceURL != null )
5436             {
5437                 getLog().debug( inputResourceName + " found in the main javadoc directory of the project." );
5438                 return FileUtils.toFile( resourceURL ).getAbsolutePath();
5439             }
5440         }
5441 
5442         classPath.clear();
5443         final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
5444         Plugin javadocPlugin = getPlugin( project, pluginId );
5445         if ( javadocPlugin != null && javadocPlugin.getDependencies() != null )
5446         {
5447             List<Dependency> dependencies = javadocPlugin.getDependencies();
5448             for ( Dependency dependency : dependencies )
5449             {
5450                 JavadocPathArtifact javadocPathArtifact = new JavadocPathArtifact();
5451                 javadocPathArtifact.setGroupId( dependency.getGroupId() );
5452                 javadocPathArtifact.setArtifactId( dependency.getArtifactId() );
5453                 javadocPathArtifact.setVersion( dependency.getVersion() );
5454                 Artifact artifact = null;
5455                 try
5456                 {
5457                     artifact = createAndResolveArtifact( javadocPathArtifact );
5458                 }
5459                 catch ( Exception e )
5460                 {
5461                     logError( "Unable to retrieve the dependency: " + dependency + ". Ignored.", e );
5462                 }
5463 
5464                 if ( artifact != null && artifact.getFile().exists() )
5465                 {
5466                     classPath.add( artifact.getFile().getAbsolutePath() );
5467                 }
5468             }
5469             resourceURL = getResource( classPath, inputResourceName );
5470             if ( resourceURL != null )
5471             {
5472                 getLog().debug( inputResourceName + " found in javadoc plugin dependencies." );
5473                 try
5474                 {
5475                     JavadocUtil.copyResource( resourceURL, outputFile );
5476 
5477                     return outputFile.getAbsolutePath();
5478                 }
5479                 catch ( IOException e )
5480                 {
5481                     logError( "IOException: " + e.getMessage(), e );
5482                 }
5483             }
5484         }
5485 
5486         getLog().warn( "Unable to find the resource '" + inputResourceName + "'. Using default Javadoc resources." );
5487 
5488         return null;
5489     }
5490 
5491     /**
5492      * @param classPath a not null String list of files where resource will be look up.
5493      * @param resource  a not null ressource to find in the class path.
5494      * @return the resource from the given classpath or null if not found
5495      * @see ClassLoader#getResource(String)
5496      * @since 2.6
5497      */
5498     private URL getResource( final List<String> classPath, final String resource )
5499     {
5500         List<URL> urls = new ArrayList<>( classPath.size() );
5501         for ( String filename : classPath )
5502         {
5503             try
5504             {
5505                 urls.add( new File( filename ).toURL() );
5506             }
5507             catch ( MalformedURLException e )
5508             {
5509                 getLog().error( "MalformedURLException: " + e.getMessage() );
5510             }
5511         }
5512 
5513         ClassLoader javadocClassLoader = new URLClassLoader( urls.toArray( new URL[urls.size()] ), null );
5514 
5515         return javadocClassLoader.getResource( resource );
5516     }
5517 
5518     /**
5519      * Get the full javadoc goal. Loads the plugin's pom.properties to get the current plugin version.
5520      *
5521      * @return <code>org.apache.maven.plugins:maven-javadoc-plugin:CURRENT_VERSION:[test-]javadoc</code>
5522      */
5523     private String getFullJavadocGoal()
5524     {
5525         String javadocPluginVersion = null;
5526         InputStream resourceAsStream = null;
5527         try
5528         {
5529             String resource = "META-INF/maven/org.apache.maven.plugins/maven-javadoc-plugin/pom.properties";
5530             resourceAsStream = AbstractJavadocMojo.class.getClassLoader().getResourceAsStream( resource );
5531 
5532             if ( resourceAsStream != null )
5533             {
5534                 Properties properties = new Properties();
5535                 properties.load( resourceAsStream );
5536                 resourceAsStream.close();
5537                 resourceAsStream = null;
5538                 if ( StringUtils.isNotEmpty( properties.getProperty( "version" ) ) )
5539                 {
5540                     javadocPluginVersion = properties.getProperty( "version" );
5541                 }
5542             }
5543         }
5544         catch ( IOException e )
5545         {
5546             // nop
5547         }
5548         finally
5549         {
5550             IOUtil.close( resourceAsStream );
5551         }
5552 
5553         StringBuilder sb = new StringBuilder();
5554 
5555         sb.append( "org.apache.maven.plugins:maven-javadoc-plugin:" );
5556         if ( StringUtils.isNotEmpty( javadocPluginVersion ) )
5557         {
5558             sb.append( javadocPluginVersion ).append( ":" );
5559         }
5560 
5561         if ( this instanceof TestJavadocReport )
5562         {
5563             sb.append( "test-javadoc" );
5564         }
5565         else
5566         {
5567             sb.append( "javadoc" );
5568         }
5569 
5570         return sb.toString();
5571     }
5572 
5573     /**
5574      * Using Maven, a Javadoc link is given by <code>${project.url}/apidocs</code>.
5575      *
5576      * @return the detected Javadoc links using the Maven conventions for all modules defined in the current project
5577      *         or an empty list.
5578      * @throws MavenReportException if any
5579      * @see #detectOfflineLinks
5580      * @see #reactorProjects
5581      * @since 2.6
5582      */
5583     private List<OfflineLink> getModulesLinks()
5584         throws MavenReportException
5585     {
5586         if ( !detectOfflineLinks || isAggregator() || reactorProjects == null )
5587         {
5588             return Collections.emptyList();
5589         }
5590 
5591         getLog().debug( "Trying to add links for modules..." );
5592 
5593         Set<String> dependencyArtifactIds = new HashSet<>();
5594         final Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
5595         for ( Artifact artifact : dependencyArtifacts )
5596         {
5597             dependencyArtifactIds.add( artifact.getId() );
5598         }
5599 
5600         List<OfflineLink> modulesLinks = new ArrayList<>();
5601         String javadocDirRelative = PathUtils.toRelative( project.getBasedir(), getOutputDirectory() );
5602         for ( MavenProject p : reactorProjects )
5603         {
5604             if ( !dependencyArtifactIds.contains( p.getArtifact().getId() ) || ( p.getUrl() == null ) )
5605             {
5606                 continue;
5607             }
5608 
5609             File location = new File( p.getBasedir(), javadocDirRelative );
5610 
5611             if ( !location.exists() )
5612             {
5613                 if ( getLog().isDebugEnabled() )
5614                 {
5615                     getLog().debug( "Javadoc directory not found: " + location );
5616                 }
5617 
5618                 String javadocGoal = getFullJavadocGoal();
5619                 getLog().info(
5620                     "The goal '" + javadocGoal + "' has not been previously called for the module: '" + p.getId()
5621                         + "'. Trying to invoke it..." );
5622 
5623                 File invokerDir = new File( project.getBuild().getDirectory(), "invoker" );
5624                 invokerDir.mkdirs();
5625                 File invokerLogFile = FileUtils.createTempFile( "maven-javadoc-plugin", ".txt", invokerDir );
5626                 try
5627                 {
5628                     JavadocUtil.invokeMaven( getLog(), new File( localRepository.getBasedir() ), p.getFile(),
5629                                              Collections.singletonList( javadocGoal ), null, invokerLogFile );
5630                 }
5631                 catch ( MavenInvocationException e )
5632                 {
5633                     logError( "MavenInvocationException: " + e.getMessage(), e );
5634 
5635                     String invokerLogContent = JavadocUtil.readFile( invokerLogFile, null /* platform encoding */ );
5636 
5637                     // TODO: Why are we only interested in cases where the JVM won't start?
5638                     // [MJAVADOC-275][jdcasey] I changed the logic here to only throw an error WHEN
5639                     //   the JVM won't start (opposite of what it was).
5640                     if ( invokerLogContent != null && invokerLogContent.contains( JavadocUtil.ERROR_INIT_VM ) )
5641                     {
5642                         throw new MavenReportException( e.getMessage(), e );
5643                     }
5644                 }
5645                 finally
5646                 {
5647                     // just create the directory to prevent repeated invocations..
5648                     if ( !location.exists() )
5649                     {
5650                         getLog().warn( "Creating fake javadoc directory to prevent repeated invocations: " + location );
5651                         location.mkdirs();
5652                     }
5653                 }
5654             }
5655 
5656             if ( location.exists() )
5657             {
5658                 String url = getJavadocLink( p );
5659 
5660                 OfflineLink ol = new OfflineLink();
5661                 ol.setUrl( url );
5662                 ol.setLocation( location.getAbsolutePath() );
5663 
5664                 if ( getLog().isDebugEnabled() )
5665                 {
5666                     getLog().debug( "Added Javadoc offline link: " + url + " for the module: " + p.getId() );
5667                 }
5668 
5669                 modulesLinks.add( ol );
5670             }
5671         }
5672 
5673         return modulesLinks;
5674     }
5675 
5676     /**
5677      * Using Maven, a Javadoc link is given by <code>${project.url}/apidocs</code>.
5678      *
5679      * @return the detected Javadoc links using the Maven conventions for all dependencies defined in the current
5680      *         project or an empty list.
5681      * @see #detectLinks
5682      * @see #isValidJavadocLink(String)
5683      * @since 2.6
5684      */
5685     private List<String> getDependenciesLinks()
5686     {
5687         if ( !detectLinks )
5688         {
5689             return Collections.emptyList();
5690         }
5691 
5692         getLog().debug( "Trying to add links for dependencies..." );
5693 
5694         List<String> dependenciesLinks = new ArrayList<>();
5695 
5696         final Set<Artifact> dependencies = project.getDependencyArtifacts();
5697         for ( Artifact artifact : dependencies )
5698         {
5699             if ( artifact.getFile() == null || !artifact.getFile().exists() )
5700             {
5701                 continue;
5702             }
5703 
5704             try
5705             {
5706                 MavenProject artifactProject =
5707                     mavenProjectBuilder.build( artifact, session.getProjectBuildingRequest() ).getProject();
5708 
5709                 if ( StringUtils.isNotEmpty( artifactProject.getUrl() ) )
5710                 {
5711                     String url = getJavadocLink( artifactProject );
5712 
5713                     if ( isValidJavadocLink( url, true ) )
5714                     {
5715                         getLog().debug( "Added Javadoc link: " + url + " for " + artifactProject.getId() );
5716 
5717                         dependenciesLinks.add( url );
5718                     }
5719                 }
5720             }
5721             catch ( ProjectBuildingException e )
5722             {
5723                 logError( "ProjectBuildingException for " + artifact.toString() + ": " + e.getMessage(), e );
5724             }
5725         }
5726 
5727         return dependenciesLinks;
5728     }
5729 
5730     /**
5731      * @return if {@link #detectJavaApiLink}, the Java API link based on the {@link #javaApiLinks} properties and the
5732      *         value of the <code>source</code> parameter in the
5733      *         <code>org.apache.maven.plugins:maven-compiler-plugin</code>
5734      *         defined in <code>${project.build.plugins}</code> or in <code>${project.build.pluginManagement}</code>,
5735      *         or the {@link #javadocRuntimeVersion}, or <code>null</code> if not defined.
5736      * @see #detectJavaApiLink
5737      * @see #javaApiLinks
5738      * @see #DEFAULT_JAVA_API_LINKS
5739      * @see <a href="http://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source">source parameter</a>
5740      * @since 2.6
5741      */
5742     private OfflineLink getDefaultJavadocApiLink()
5743     {
5744         if ( !detectJavaApiLink )
5745         {
5746             return null;
5747         }
5748 
5749         final String pluginId = "org.apache.maven.plugins:maven-compiler-plugin";
5750         JavaVersion sourceVersion = javadocRuntimeVersion;
5751         String sourceConfigured = getPluginParameter( project, pluginId, "source" );
5752         if ( sourceConfigured != null )
5753         {
5754             try
5755             {
5756                 sourceVersion = JavaVersion.parse( sourceConfigured );
5757             }
5758             catch ( NumberFormatException e )
5759             {
5760                 getLog().debug(
5761                     "NumberFormatException for the source parameter in the maven-compiler-plugin. " + "Ignored it", e );
5762             }
5763         }
5764         else
5765         {
5766             getLog().debug( "No maven-compiler-plugin defined in ${build.plugins} or in "
5767                                 + "${project.build.pluginManagement} for the " + project.getId()
5768                                 + ". Added Javadoc API link according the javadoc executable version i.e.: "
5769                                 + javadocRuntimeVersion );
5770         }
5771 
5772         String apiVersion;
5773         
5774         Matcher apiMatcher = Pattern.compile( "(1\\.\\d|\\d\\d*)" ).matcher( sourceVersion.toString() );
5775         if ( apiMatcher.find() )
5776         {
5777             apiVersion = apiMatcher.group( 1 );
5778         }
5779         else
5780         {
5781             apiVersion = null;
5782         }
5783 
5784         String javaApiLink = javaApiLinks.getProperty( "api_" + apiVersion, null );
5785 
5786         if ( getLog().isDebugEnabled() )
5787         {
5788             if ( StringUtils.isNotEmpty( javaApiLink ) )
5789             {
5790                 getLog().debug( "Found Java API link: " + javaApiLink );
5791             }
5792             else
5793             {
5794                 getLog().debug( "No Java API link found." );
5795             }
5796         }
5797 
5798         if ( javaApiLink == null )
5799         {
5800             return null;
5801         }
5802 
5803         File javaApiPackageListFile = new File( getJavadocOptionsFile().getParentFile(), "package-list" );
5804 
5805         OfflineLink link = new OfflineLink();
5806         link.setLocation( javaApiPackageListFile.getParentFile().getAbsolutePath() );
5807         link.setUrl( javaApiLink );
5808 
5809         InputStream in = null;
5810         OutputStream out = null;
5811         try
5812         {
5813             in = this.getClass().getResourceAsStream( "java-api-package-list-" + apiVersion );
5814             out = new FileOutputStream( javaApiPackageListFile );
5815             IOUtil.copy( in, out );
5816             out.close();
5817             out = null;
5818             in.close();
5819             in = null;
5820         }
5821         catch ( IOException ioe )
5822         {
5823             logError( "Can't get java-api-package-list-" + apiVersion + ": " + ioe.getMessage(), ioe );
5824             return null;
5825         }
5826         finally
5827         {
5828             IOUtil.close( in );
5829             IOUtil.close( out );
5830         }
5831 
5832         return link;
5833     }
5834 
5835     /**
5836      * Follows all of the given links, and returns their last redirect locations. Ordering is kept.
5837      * This is necessary because javadoc tool doesn't follow links, see JDK-8190312 (MJAVADOC-427, MJAVADOC-487)
5838      *
5839      * @param links Links to follow.
5840      * @return Last redirect location of all the links.
5841      */
5842     private Set<String> followLinks( Set<String> links )
5843     {
5844         Set<String> redirectLinks = new LinkedHashSet<>( links.size() );
5845         for ( String link : links )
5846         {
5847             try
5848             {
5849                 redirectLinks.add( JavadocUtil.getRedirectUrl( new URI( link ).toURL(), settings ).toString() );
5850             }
5851             catch ( Exception e )
5852             {
5853                 // only print in debug, it should have been logged already in warn/error because link isn't valid
5854                 getLog().debug( "Could not follow " + link + ". Reason: " + e.getMessage() );
5855             }
5856         }
5857         return redirectLinks;
5858     }
5859 
5860     /**
5861      * @param link not null
5862      * @param detecting <code>true</code> if the link is generated by
5863      * <code>detectLinks</code>, or <code>false</code> otherwise
5864      * @return <code>true</code> if the link has a <code>/package-list</code>, <code>false</code> otherwise.
5865      * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javadoc.html#package-list">
5866      *      package-list spec</a>
5867      * @since 2.6
5868      */
5869     protected boolean isValidJavadocLink( String link, boolean detecting )
5870     {
5871         try
5872         {
5873             URI linkUri;
5874             if ( link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "http:" ) || link.trim().toLowerCase(
5875                 Locale.ENGLISH ).startsWith( "https:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith(
5876                 "ftp:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "file:" ) )
5877             {
5878                 linkUri = new URI( link + "/package-list" );
5879             }
5880             else
5881             {
5882                 // links can be relative paths or files
5883                 File dir = new File( link );
5884                 if ( !dir.isAbsolute() )
5885                 {
5886                     dir = new File( getOutputDirectory(), link );
5887                 }
5888                 if ( !dir.isDirectory() )
5889                 {
5890                     if ( detecting )
5891                     {
5892                         getLog().warn( "The given File link: " + dir + " is not a dir." );
5893                     }
5894                     else
5895                     {
5896                         getLog().error( "The given File link: " + dir + " is not a dir." );
5897                     }
5898                 }
5899                 linkUri = new File( dir, "package-list" ).toURI();
5900             }
5901 
5902             if ( !JavadocUtil.isValidPackageList( linkUri.toURL(), settings, validateLinks ) )
5903             {
5904                 if ( getLog().isErrorEnabled() )
5905                 {
5906                     if ( detecting )
5907                     {
5908                         getLog().warn( "Invalid link: " + link + "/package-list. Ignored it." );
5909                     }
5910                     else
5911                     {
5912                         getLog().error( "Invalid link: " + link + "/package-list. Ignored it." );
5913                     }
5914                 }
5915 
5916                 return false;
5917             }
5918 
5919             return true;
5920         }
5921         catch ( URISyntaxException e )
5922         {
5923             if ( getLog().isErrorEnabled() )
5924             {
5925                 if ( detecting )
5926                 {
5927                     getLog().warn( "Malformed link: " + link + "/package-list. Ignored it." );
5928                 }
5929                 else
5930                 {
5931                     getLog().error( "Malformed link: " + link + "/package-list. Ignored it." );
5932                 }
5933             }
5934             return false;
5935         }
5936         catch ( IOException e )
5937         {
5938             if ( getLog().isErrorEnabled() )
5939             {
5940                 if ( detecting )
5941                 {
5942                     getLog().warn( "Error fetching link: " + link + "/package-list. Ignored it." );
5943                 }
5944                 else
5945                 {
5946                     getLog().error( "Error fetching link: " + link + "/package-list. Ignored it." );
5947                 }
5948             }
5949             return false;
5950         }
5951     }
5952 
5953     /**
5954      * Write a debug javadoc script in case of command line error or in debug mode.
5955      *
5956      * @param cmdLine                the current command line as string, not null.
5957      * @param javadocOutputDirectory the output dir, not null.
5958      * @see #executeJavadocCommandLine(Commandline, File)
5959      * @since 2.6
5960      */
5961     private void writeDebugJavadocScript( String cmdLine, File javadocOutputDirectory )
5962     {
5963         File commandLineFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
5964         commandLineFile.getParentFile().mkdirs();
5965 
5966         try
5967         {
5968             FileUtils.fileWrite( commandLineFile.getAbsolutePath(), null /* platform encoding */, cmdLine );
5969 
5970             if ( !SystemUtils.IS_OS_WINDOWS )
5971             {
5972                 Runtime.getRuntime().exec( new String[]{ "chmod", "a+x", commandLineFile.getAbsolutePath() } );
5973             }
5974         }
5975         catch ( IOException e )
5976         {
5977             logError( "Unable to write '" + commandLineFile.getName() + "' debug script file", e );
5978         }
5979     }
5980 
5981     /**
5982      * Check if the Javadoc JVM is correctly started or not.
5983      *
5984      * @param output the command line output, not null.
5985      * @return <code>true</code> if Javadoc output command line contains Javadoc word, <code>false</code> otherwise.
5986      * @see #executeJavadocCommandLine(Commandline, File)
5987      * @since 2.6.1
5988      */
5989     private boolean isJavadocVMInitError( String output )
5990     {
5991         /*
5992          * see main.usage and main.Building_tree keys from
5993          * com.sun.tools.javadoc.resources.javadoc bundle in tools.jar
5994          */
5995         return !( output.contains( "Javadoc" ) || output.contains( "javadoc" ) );
5996     }
5997 
5998     // ----------------------------------------------------------------------
5999     // Static methods
6000     // ----------------------------------------------------------------------
6001 
6002     /**
6003      * @param p not null
6004      * @return the javadoc link based on the project url i.e. <code>${project.url}/${destDir}</code> where
6005      *         <code>destDir</code> is configued in the Javadoc plugin configuration (<code>apidocs</code> by default).
6006      * @since 2.6
6007      */
6008     private static String getJavadocLink( MavenProject p )
6009     {
6010         if ( p.getUrl() == null )
6011         {
6012             return null;
6013         }
6014 
6015         String url = cleanUrl( p.getUrl() );
6016         String destDir = "apidocs"; // see JavadocReport#destDir
6017 
6018         final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
6019         String destDirConfigured = getPluginParameter( p, pluginId, "destDir" );
6020         if ( destDirConfigured != null )
6021         {
6022             destDir = destDirConfigured;
6023         }
6024 
6025         return url + "/" + destDir;
6026     }
6027 
6028     /**
6029      * @param url could be null.
6030      * @return the url cleaned or empty if url was null.
6031      * @since 2.6
6032      */
6033     private static String cleanUrl( String url )
6034     {
6035         if ( url == null )
6036         {
6037             return "";
6038         }
6039 
6040         url = url.trim();
6041         while ( url.endsWith( "/" ) )
6042         {
6043             url = url.substring( 0, url.lastIndexOf( "/" ) );
6044         }
6045 
6046         return url;
6047     }
6048 
6049     /**
6050      * @param p        not null
6051      * @param pluginId not null key of the plugin defined in {@link org.apache.maven.model.Build#getPluginsAsMap()}
6052      *                 or in {@link org.apache.maven.model.PluginManagement#getPluginsAsMap()}
6053      * @return the Maven plugin defined in <code>${project.build.plugins}</code> or in
6054      *         <code>${project.build.pluginManagement}</code>, or <code>null</code> if not defined.
6055      * @since 2.6
6056      */
6057     private static Plugin getPlugin( MavenProject p, String pluginId )
6058     {
6059         if ( ( p.getBuild() == null ) || ( p.getBuild().getPluginsAsMap() == null ) )
6060         {
6061             return null;
6062         }
6063 
6064         Plugin plugin = p.getBuild().getPluginsAsMap().get( pluginId );
6065 
6066         if ( ( plugin == null ) && ( p.getBuild().getPluginManagement() != null ) && (
6067             p.getBuild().getPluginManagement().getPluginsAsMap() != null ) )
6068         {
6069             plugin = p.getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
6070         }
6071 
6072         return plugin;
6073     }
6074 
6075     /**
6076      * @param p        not null
6077      * @param pluginId not null
6078      * @param param    not null
6079      * @return the simple parameter as String defined in the plugin configuration by <code>param</code> key
6080      *         or <code>null</code> if not found.
6081      * @since 2.6
6082      */
6083     private static String getPluginParameter( MavenProject p, String pluginId, String param )
6084     {
6085 //        p.getGoalConfiguration( pluginGroupId, pluginArtifactId, executionId, goalId );
6086         Plugin plugin = getPlugin( p, pluginId );
6087         if ( plugin != null )
6088         {
6089             Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
6090             if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
6091                 && StringUtils.isNotEmpty( xpp3Dom.getChild( param ).getValue() ) )
6092             {
6093                 return xpp3Dom.getChild( param ).getValue();
6094             }
6095         }
6096 
6097         return null;
6098     }
6099 
6100     /**
6101      * Construct the output file for the generated javadoc-options XML file, after creating the
6102      * javadocOptionsDir if necessary. This method does NOT write to the file in question.
6103      *
6104      * @return The options {@link File} file.
6105      * @since 2.7
6106      */
6107     protected final File getJavadocOptionsFile()
6108     {
6109         if ( javadocOptionsDir != null && !javadocOptionsDir.exists() )
6110         {
6111             javadocOptionsDir.mkdirs();
6112         }
6113 
6114         return new File( javadocOptionsDir, "javadoc-options-" + getAttachmentClassifier() + ".xml" );
6115     }
6116 
6117     /**
6118      * Generate a javadoc-options XML file, for either bundling with a javadoc-resources artifact OR
6119      * supplying to a distro module in a includeDependencySources configuration, so the javadoc options
6120      * from this execution can be reconstructed and merged in the distro build.
6121      *
6122      * @return {@link JavadocOptions}
6123      * @throws IOException {@link IOException}
6124      * @since 2.7
6125      */
6126     protected final JavadocOptions buildJavadocOptions()
6127         throws IOException
6128     {
6129         JavadocOptions options = new JavadocOptions();
6130 
6131         options.setBootclasspathArtifacts( toList( bootclasspathArtifacts ) );
6132         options.setDocfilesSubdirsUsed( docfilessubdirs );
6133         options.setDocletArtifacts( toList( docletArtifact, docletArtifacts ) );
6134         options.setExcludedDocfilesSubdirs( excludedocfilessubdir );
6135         options.setExcludePackageNames( toList( excludePackageNames ) );
6136         options.setGroups( toList( groups ) );
6137         options.setLinks( links );
6138         options.setOfflineLinks( toList( offlineLinks ) );
6139         options.setResourcesArtifacts( toList( resourcesArtifacts ) );
6140         options.setTagletArtifacts( toList( tagletArtifact, tagletArtifacts ) );
6141         options.setTaglets( toList( taglets ) );
6142         options.setTags( toList( tags ) );
6143 
6144         if ( getProject() != null && getJavadocDirectory() != null )
6145         {
6146             options.setJavadocResourcesDirectory(
6147                 toRelative( getProject().getBasedir(), getJavadocDirectory().getAbsolutePath() ) );
6148         }
6149 
6150         File optionsFile = getJavadocOptionsFile();
6151         
6152         try ( Writer writer = WriterFactory.newXmlWriter( optionsFile ) )
6153         {
6154             new JavadocOptionsXpp3Writer().write( writer, options );
6155         }
6156 
6157         return options;
6158     }
6159 
6160     /**
6161      * Override this if you need to provide a bundle attachment classifier, as in the case of test
6162      * javadocs.
6163      * @return The attachment classifier.
6164      */
6165     protected String getAttachmentClassifier()
6166     {
6167         return JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER;
6168     }
6169 
6170     /**
6171      * Logs an error with throwable content only if in debug.
6172      *
6173      * @param message The message which should be announced.
6174      * @param t The throwable part of the message.
6175      */
6176     protected void logError( String message, Throwable t )
6177     {
6178         if ( getLog().isDebugEnabled() )
6179         {
6180             getLog().error( message, t );
6181         }
6182         else
6183         {
6184             getLog().error( message );
6185         }
6186     }
6187 
6188     /**
6189      * @param prefix The prefix of the exception.
6190      * @param e The exception.
6191      * @throws MojoExecutionException {@link MojoExecutionException}
6192      */
6193     protected void failOnError( String prefix, Exception e )
6194         throws MojoExecutionException
6195     {
6196         if ( failOnError )
6197         {
6198             if ( e instanceof RuntimeException )
6199             {
6200                 throw (RuntimeException) e;
6201             }
6202             throw new MojoExecutionException( prefix + ": " + e.getMessage(), e );
6203         }
6204 
6205         getLog().error( prefix + ": " + e.getMessage(), e );
6206     }
6207 }