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