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