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