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