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