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