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