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