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