You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gn...@apache.org on 2020/01/27 16:44:29 UTC
[camel] 03/07: [CAMEL-14444] Build speed improvements: include a
copy of the latest javadoc mojo
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 6c9c0be5b4d079fe48793a32befe8ad196b9bee3
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Jan 17 16:08:27 2020 +0100
[CAMEL-14444] Build speed improvements: include a copy of the latest javadoc mojo
---
parent/pom.xml | 2 +-
.../maven/plugins/javadoc/AbstractJavadocMojo.java | 6820 ++++++++++++++++++++
.../apache/maven/plugins/javadoc/StaleHelper.java | 181 +
3 files changed, 7002 insertions(+), 1 deletion(-)
diff --git a/parent/pom.xml b/parent/pom.xml
index 320dc37..2e33eb5 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -441,7 +441,7 @@
<maven-checkstyle-version>8.26</maven-checkstyle-version>
<maven-owasp-plugin-version>5.3.0</maven-owasp-plugin-version>
<maven-jar-plugin-version>3.1.2</maven-jar-plugin-version>
- <maven-javadoc-plugin-version>3.0.1</maven-javadoc-plugin-version>
+ <maven-javadoc-plugin-version>3.1.1</maven-javadoc-plugin-version>
<maven-jboss-as-maven-plugin-version>7.9.Final</maven-jboss-as-maven-plugin-version>
<maven-plugin-plugin-version>3.6.0</maven-plugin-plugin-version>
<maven-plugin-annotations-version>3.6.0</maven-plugin-annotations-version>
diff --git a/tooling/maven/camel-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java b/tooling/maven/camel-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java
new file mode 100644
index 0000000..c27f3bc
--- /dev/null
+++ b/tooling/maven/camel-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java
@@ -0,0 +1,6820 @@
+package org.apache.maven.plugins.javadoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.ArtifactUtils;
+import org.apache.maven.artifact.handler.ArtifactHandler;
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.javadoc.options.BootclasspathArtifact;
+import org.apache.maven.plugins.javadoc.options.DocletArtifact;
+import org.apache.maven.plugins.javadoc.options.Group;
+import org.apache.maven.plugins.javadoc.options.JavadocOptions;
+import org.apache.maven.plugins.javadoc.options.JavadocPathArtifact;
+import org.apache.maven.plugins.javadoc.options.OfflineLink;
+import org.apache.maven.plugins.javadoc.options.ResourcesArtifact;
+import org.apache.maven.plugins.javadoc.options.Tag;
+import org.apache.maven.plugins.javadoc.options.Taglet;
+import org.apache.maven.plugins.javadoc.options.TagletArtifact;
+import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Writer;
+import org.apache.maven.plugins.javadoc.resolver.JavadocBundle;
+import org.apache.maven.plugins.javadoc.resolver.ResourceResolver;
+import org.apache.maven.plugins.javadoc.resolver.SourceResolverConfig;
+import org.apache.maven.project.DefaultProjectBuildingRequest;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingException;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.reporting.MavenReportException;
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
+import org.apache.maven.shared.artifact.filter.resolve.AndFilter;
+import org.apache.maven.shared.artifact.filter.resolve.PatternExclusionsFilter;
+import org.apache.maven.shared.artifact.filter.resolve.PatternInclusionsFilter;
+import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
+import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
+import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
+import org.apache.maven.shared.invoker.MavenInvocationException;
+import org.apache.maven.toolchain.Toolchain;
+import org.apache.maven.toolchain.ToolchainManager;
+import org.apache.maven.wagon.PathUtils;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.UnArchiver;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
+import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
+import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
+import org.codehaus.plexus.languages.java.jpms.LocationManager;
+import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
+import org.codehaus.plexus.languages.java.version.JavaVersion;
+import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.WriterFactory;
+import org.codehaus.plexus.util.cli.CommandLineException;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.codehaus.plexus.util.cli.Commandline;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import static org.apache.maven.plugins.javadoc.JavadocUtil.toRelative;
+import static org.apache.maven.plugins.javadoc.JavadocUtil.toList;
+import static org.apache.maven.plugins.javadoc.JavadocUtil.isEmpty;
+import static org.apache.maven.plugins.javadoc.JavadocUtil.isNotEmpty;
+
+/**
+ * Base class with majority of Javadoc functionalities.
+ *
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html">
+ * The Java API Documentation Generator, 7</a>
+ * @since 2.0
+ */
+public abstract class AbstractJavadocMojo
+ extends AbstractMojo
+{
+ /**
+ * Classifier used in the name of the javadoc-options XML file, and in the resources bundle
+ * artifact that gets attached to the project. This one is used for non-test javadocs.
+ *
+ * @see #TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER
+ * @since 2.7
+ */
+ public static final String JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "javadoc-resources";
+
+ /**
+ * Classifier used in the name of the javadoc-options XML file, and in the resources bundle
+ * artifact that gets attached to the project. This one is used for test-javadocs.
+ *
+ * @see #JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER
+ * @since 2.7
+ */
+ public static final String TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "test-javadoc-resources";
+
+ /**
+ * The Javadoc script file name when <code>debug</code> parameter is on, i.e. javadoc.bat or javadoc.sh
+ */
+ protected static final String DEBUG_JAVADOC_SCRIPT_NAME = "javadoc." + ( SystemUtils.IS_OS_WINDOWS ? "bat" : "sh" );
+
+ /**
+ * The <code>options</code> file name in the output directory when calling:
+ * <code>javadoc.exe(or .sh) @options @packages | @argfile | @files</code>
+ */
+ protected static final String OPTIONS_FILE_NAME = "options";
+
+ /**
+ * The <code>packages</code> file name in the output directory when calling:
+ * <code>javadoc.exe(or .sh) @options @packages | @argfile | @files</code>
+ */
+ protected static final String PACKAGES_FILE_NAME = "packages";
+
+ /**
+ * The <code>argfile</code> file name in the output directory when calling:
+ * <code>javadoc.exe(or .sh) @options @packages | @argfile | @files</code>
+ */
+ protected static final String ARGFILE_FILE_NAME = "argfile";
+
+ /**
+ * The <code>files</code> file name in the output directory when calling:
+ * <code>javadoc.exe(or .sh) @options @packages | @argfile | @files</code>
+ */
+ protected static final String FILES_FILE_NAME = "files";
+
+ /**
+ * The current class directory
+ */
+ private static final String RESOURCE_DIR = ClassUtils.getPackageName( JavadocReport.class ).replace( '.', '/' );
+
+ /**
+ * Default css file name
+ */
+ private static final String DEFAULT_CSS_NAME = "stylesheet.css";
+
+ /**
+ * Default location for css
+ */
+ private static final String RESOURCE_CSS_DIR = RESOURCE_DIR + "/css";
+
+ private static final String PACKAGE_LIST = "package-list";
+ private static final String ELEMENT_LIST = "element-list";
+
+
+ /**
+ * For Javadoc options appears since Java 1.4.
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.1.html#summary">
+ * What's New in Javadoc 1.4</a>
+ *
+ * @since 2.1
+ */
+ private static final JavaVersion SINCE_JAVADOC_1_4 = JavaVersion.parse( "1.4" );
+
+ /**
+ * For Javadoc options appears since Java 1.4.2.
+ * See <a
+ * href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
+ * What's New in Javadoc 1.4.2</a>
+ *
+ * @since 2.1
+ */
+ private static final JavaVersion SINCE_JAVADOC_1_4_2 = JavaVersion.parse( "1.4.2" );
+
+ /**
+ * For Javadoc options appears since Java 5.0.
+ * See <a
+ * href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.5.0.html#commandlineoptions">
+ * What's New in Javadoc 5.0</a>
+ *
+ * @since 2.1
+ */
+ private static final JavaVersion SINCE_JAVADOC_1_5 = JavaVersion.parse( "1.5" );
+
+ /**
+ * For Javadoc options appears since Java 6.0.
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/index.html">
+ * Javadoc Technology</a>
+ *
+ * @since 2.4
+ */
+ private static final JavaVersion SINCE_JAVADOC_1_6 = JavaVersion.parse( "1.6" );
+
+ /**
+ * For Javadoc options appears since Java 8.0.
+ * See <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/index.html">
+ * Javadoc Technology</a>
+ *
+ * @since 3.0.0
+ */
+ private static final JavaVersion SINCE_JAVADOC_1_8 = JavaVersion.parse( "1.8" );
+
+ /**
+ *
+ */
+ private static final JavaVersion JAVA_VERSION = JavaVersion.JAVA_SPECIFICATION_VERSION;
+
+ // ----------------------------------------------------------------------
+ // Mojo components
+ // ----------------------------------------------------------------------
+
+ /**
+ * Archiver manager
+ *
+ * @since 2.5
+ */
+ @Component
+ private ArchiverManager archiverManager;
+
+ @Component
+ private ResourceResolver resourceResolver;
+
+ @Component
+ private ArtifactResolver artifactResolver;
+
+ @Component
+ private ArtifactHandlerManager artifactHandlerManager;
+
+ @Component
+ private DependencyResolver dependencyResolver;
+
+ /**
+ * Project builder
+ *
+ * @since 3.0
+ */
+ @Component
+ private ProjectBuilder mavenProjectBuilder;
+
+ /** */
+ @Component
+ private ToolchainManager toolchainManager;
+
+ final LocationManager locationManager = new LocationManager();
+
+ // ----------------------------------------------------------------------
+ // Mojo parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The current build session instance. This is used for
+ * toolchain manager API calls.
+ */
+ @Parameter( defaultValue = "${session}", readonly = true, required = true )
+ protected MavenSession session;
+
+ /**
+ * The Maven Settings.
+ *
+ * @since 2.3
+ */
+ @Parameter( defaultValue = "${settings}", readonly = true, required = true )
+ private Settings settings;
+
+ /**
+ * The Maven Project Object
+ */
+ @Parameter( defaultValue = "${project}", readonly = true, required = true )
+ protected MavenProject project;
+
+ @Parameter( defaultValue = "${mojoExecution}", readonly = true )
+ private MojoExecution mojo;
+
+ /**
+ * Specify if the Javadoc should operate in offline mode.
+ */
+ @Parameter( defaultValue = "${settings.offline}", required = true, readonly = true )
+ private boolean isOffline;
+
+ /**
+ * Specifies the Javadoc resources directory to be included in the Javadoc (i.e. package.html, images...).
+ * <br/>
+ * Could be used in addition of <code>docfilessubdirs</code> parameter.
+ * <br/>
+ * See <a href="#docfilessubdirs">docfilessubdirs</a>.
+ *
+ * @see #docfilessubdirs
+ * @since 2.1
+ */
+ @Parameter( defaultValue = "${basedir}/src/main/javadoc" )
+ private File javadocDirectory;
+
+ /**
+ * Set an additional option(s) on the command line. All input will be passed as-is to the
+ * {@code @options} file. You must take care of quoting and escaping. Useful for a custom doclet.
+ *
+ * @since 3.0.0
+ */
+ @Parameter
+ private String[] additionalOptions;
+
+ /**
+ * Set an additional Javadoc option(s) (i.e. JVM options) on the command line.
+ * Example:
+ * <pre>
+ * <additionalJOption>-J-Xss128m</additionalJOption>
+ * </pre>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#J">Jflag</a>.
+ * <br/>
+ * See <a href="http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp">vmoptions</a>.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/net/properties.html">Networking
+ * Properties</a>.
+ *
+ * @since 2.3
+ */
+ @Parameter( property = "additionalJOption" )
+ private String additionalJOption;
+
+ /**
+ * Set additional JVM options for the execution of the javadoc command via the '-J' option to javadoc.
+ * Example:
+ * <pre>
+ * <additionalJOptions>
+ * <additionalJOption>-J-Xmx1g </additionalJOption>
+ * </additionalJOptions>
+ * </pre>
+ * @since 2.9
+ */
+ @Parameter
+ private String[] additionalJOptions;
+
+ /**
+ * A list of artifacts containing resources which should be copied into the
+ * Javadoc output directory (like stylesheets, icons, etc.).
+ * <br/>
+ * Example:
+ * <pre>
+ * <resourcesArtifacts>
+ * <resourcesArtifact>
+ * <groupId>external.group.id</groupId>
+ * <artifactId>external-resources</artifactId>
+ * <version>1.0</version>
+ * </resourcesArtifact>
+ * </resourcesArtifacts>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/ResourcesArtifact.html">Javadoc</a>.
+ * <br/>
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "resourcesArtifacts" )
+ private ResourcesArtifact[] resourcesArtifacts;
+
+ /**
+ * The local repository where the artifacts are located.
+ */
+ @Parameter( property = "localRepository" )
+ private ArtifactRepository localRepository;
+
+ /**
+ * The projects in the reactor for aggregation report.
+ */
+ @Parameter( property = "reactorProjects", readonly = true )
+ private List<MavenProject> reactorProjects;
+
+ /**
+ * Set this to <code>true</code> to debug the Javadoc plugin. With this, <code>javadoc.bat(or.sh)</code>,
+ * <code>options</code>, <code>@packages</code> or <code>argfile</code> files are provided in the output directory.
+ * <br/>
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "debug", defaultValue = "false" )
+ private boolean debug;
+
+ /**
+ * Sets the absolute path of the Javadoc Tool executable to use. Since version 2.5, a mere directory specification
+ * is sufficient to have the plugin use "javadoc" or "javadoc.exe" respectively from this directory.
+ *
+ * @since 2.3
+ */
+ @Parameter( property = "javadocExecutable" )
+ private String javadocExecutable;
+
+ /**
+ * Version of the Javadoc Tool executable to use, ex. "1.3", "1.5".
+ *
+ * @since 2.3
+ */
+ @Parameter( property = "javadocVersion" )
+ private String javadocVersion;
+
+ /**
+ * Version of the Javadoc Tool executable to use.
+ */
+ private JavaVersion javadocRuntimeVersion;
+
+ /**
+ * Specifies whether the Javadoc generation should be skipped.
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "maven.javadoc.skip", defaultValue = "false" )
+ protected boolean skip;
+
+ /**
+ * Specifies if the build will fail if there are errors during javadoc execution or not.
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "maven.javadoc.failOnError", defaultValue = "true" )
+ protected boolean failOnError;
+
+
+ /**
+ * Specifies if the build will fail if there are warning during javadoc execution or not.
+ *
+ * @since 3.0.1
+ */
+ @Parameter( property = "maven.javadoc.failOnWarnings", defaultValue = "false" )
+ protected boolean failOnWarnings;
+
+ /**
+ * Specifies to use the
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#standard">
+ * options provided by the Standard Doclet</a> for a custom doclet.
+ * <br>
+ * Example:
+ * <pre>
+ * <docletArtifacts>
+ * <docletArtifact>
+ * <groupId>com.sun.tools.doclets</groupId>
+ * <artifactId>doccheck</artifactId>
+ * <version>1.2b2</version>
+ * </docletArtifact>
+ * </docletArtifacts>
+ * <useStandardDocletOptions>true</useStandardDocletOptions>
+ * </pre>
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "useStandardDocletOptions", defaultValue = "true" )
+ protected boolean useStandardDocletOptions;
+
+ /**
+ * Detect the Javadoc links for all dependencies defined in the project. The detection is based on the default
+ * Maven conventions, i.e.: <code>${project.url}/apidocs</code>.
+ * <br/>
+ * For instance, if the project has a dependency to
+ * <a href="http://commons.apache.org/lang/">Apache Commons Lang</a> i.e.:
+ * <pre>
+ * <dependency>
+ * <groupId>commons-lang</groupId>
+ * <artifactId>commons-lang</artifactId>
+ * </dependency>
+ * </pre>
+ * The added Javadoc <code>-link</code> parameter will be <code>http://commons.apache.org/lang/apidocs</code>.
+ *
+ * @see #links
+ * @since 2.6
+ */
+ @Parameter( property = "detectLinks", defaultValue = "false" )
+ private boolean detectLinks;
+
+ /**
+ * Detect the links for all modules defined in the project.
+ * <br/>
+ * If {@link #reactorProjects} is defined in a non-aggregator way, it generates default offline links
+ * between modules based on the defined project's urls. For instance, if a parent project has two projects
+ * <code>module1</code> and <code>module2</code>, the <code>-linkoffline</code> will be:
+ * <br/>
+ * The added Javadoc <code>-linkoffline</code> parameter for <b>module1</b> will be
+ * <code>/absolute/path/to/</code><b>module2</b><code>/target/site/apidocs</code>
+ * <br/>
+ * The added Javadoc <code>-linkoffline</code> parameter for <b>module2</b> will be
+ * <code>/absolute/path/to/</code><b>module1</b><code>/target/site/apidocs</code>
+ *
+ * @see #offlineLinks
+ * @since 2.6
+ */
+ @Parameter( property = "detectOfflineLinks", defaultValue = "true" )
+ private boolean detectOfflineLinks;
+
+ /**
+ * Detect the Java API link for the current build, i.e. <code>http://docs.oracle.com/javase/1.4.2/docs/api/</code>
+ * for Java source 1.4.
+ * <br/>
+ * By default, the goal detects the Javadoc API link depending the value of the <code>source</code>
+ * parameter in the <code>org.apache.maven.plugins:maven-compiler-plugin</code>
+ * (defined in <code>${project.build.plugins}</code> or in <code>${project.build.pluginManagement}</code>),
+ * or try to compute it from the {@link #javadocExecutable} version.
+ * <br/>
+ * See
+ * <a href="./apidocs/org/apache/maven/plugin/javadoc/AbstractJavadocMojo.html#DEFAULT_JAVA_API_LINKS">Javadoc</a>
+ * for the default values.
+ * <br/>
+ *
+ * @see #links
+ * @see #javaApiLinks
+ * @see #DEFAULT_JAVA_API_LINKS
+ * @since 2.6
+ */
+ @Parameter( property = "detectJavaApiLink", defaultValue = "true" )
+ private boolean detectJavaApiLink;
+
+ /**
+ * Use this parameter <b>only</b> if if you want to override the default URLs.
+ *
+ * The key should match {@code api_x}, where {@code x} matches the Java version.
+ *
+ * For example:
+ * <dl>
+ * <dt>api_1.5</dt>
+ * <dd>https://docs.oracle.com/javase/1.5.0/docs/api/</dd>
+ * <dt>api_1.8<dt>
+ * <dd>https://docs.oracle.com/javase/8/docs/api/</dd>
+ * <dt>api_9</dd>
+ * <dd>https://docs.oracle.com/javase/9/docs/api/</dd>
+ * </dl>
+ * @since 2.6
+ */
+ @Parameter( property = "javaApiLinks" )
+ private Properties javaApiLinks;
+
+ /**
+ * Flag controlling content validation of <code>package-list</code> resources. If set, the content of
+ * <code>package-list</code> resources will be validated.
+ *
+ * @since 2.8
+ */
+ @Parameter( property = "validateLinks", defaultValue = "false" )
+ private boolean validateLinks;
+
+ // ----------------------------------------------------------------------
+ // Javadoc Options - all alphabetical
+ // ----------------------------------------------------------------------
+
+ /**
+ * Specifies the paths where the boot classes reside. The <code>bootclasspath</code> can contain multiple paths
+ * by separating them with a colon (<code>:</code>) or a semi-colon (<code>;</code>).
+ * <br/>
+ * See <a
+ * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#bootclasspath">bootclasspath</a>.
+ * <br/>
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "bootclasspath" )
+ private String bootclasspath;
+
+ /**
+ * Specifies the artifacts where the boot classes reside.
+ * <br/>
+ * See <a
+ * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#bootclasspath">bootclasspath</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <bootclasspathArtifacts>
+ * <bootclasspathArtifact>
+ * <groupId>my-groupId</groupId>
+ * <artifactId>my-artifactId</artifactId>
+ * <version>my-version</version>
+ * </bootclasspathArtifact>
+ * </bootclasspathArtifacts>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/BootclasspathArtifact.html">Javadoc</a>.
+ * <br/>
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "bootclasspathArtifacts" )
+ private BootclasspathArtifact[] bootclasspathArtifacts;
+
+ /**
+ * Uses the sentence break iterator to determine the end of the first sentence.
+ * <br/>
+ * See <a
+ * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#breakiterator">breakiterator</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ * <br/>
+ */
+ @Parameter( property = "breakiterator", defaultValue = "false" )
+ private boolean breakiterator;
+
+ /**
+ * Specifies the class file that starts the doclet used in generating the documentation.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#doclet">doclet</a>.
+ */
+ @Parameter( property = "doclet" )
+ private String doclet;
+
+ /**
+ * Specifies the artifact containing the doclet starting class file (specified with the <code>-doclet</code>
+ * option).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docletpath">docletpath</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <docletArtifact>
+ * <groupId>com.sun.tools.doclets</groupId>
+ * <artifactId>doccheck</artifactId>
+ * <version>1.2b2</version>
+ * </docletArtifact>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/DocletArtifact.html">Javadoc</a>.
+ * <br/>
+ */
+ @Parameter( property = "docletArtifact" )
+ private DocletArtifact docletArtifact;
+
+ /**
+ * Specifies multiple artifacts containing the path for the doclet starting class file (specified with the
+ * <code>-doclet</code> option).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docletpath">docletpath</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <docletArtifacts>
+ * <docletArtifact>
+ * <groupId>com.sun.tools.doclets</groupId>
+ * <artifactId>doccheck</artifactId>
+ * <version>1.2b2</version>
+ * </docletArtifact>
+ * </docletArtifacts>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/DocletArtifact.html">Javadoc</a>.
+ * <br/>
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "docletArtifacts" )
+ private DocletArtifact[] docletArtifacts;
+
+ /**
+ * Specifies the path to the doclet starting class file (specified with the <code>-doclet</code> option) and
+ * any jar files it depends on. The <code>docletPath</code> can contain multiple paths by separating them with
+ * a colon (<code>:</code>) or a semi-colon (<code>;</code>).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docletpath">docletpath</a>.
+ */
+ @Parameter( property = "docletPath" )
+ private String docletPath;
+
+ /**
+ * Specifies the encoding name of the source files. If not specificed, the encoding value will be the value of the
+ * <code>file.encoding</code> system property.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#encoding">encoding</a>.
+ * <br/>
+ * <b>Note</b>: In 2.4, the default value was locked to <code>ISO-8859-1</code> to ensure reproducing build, but
+ * this was reverted in 2.5.
+ * <br/>
+ */
+ @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
+ private String encoding;
+
+ /**
+ * Unconditionally excludes the specified packages and their subpackages from the list formed by
+ * <code>-subpackages</code>. Multiple packages can be separated by commas (<code>,</code>), colons (<code>:</code>)
+ * or semicolons (<code>;</code>).
+ * <p>
+ * Wildcards work as followed:
+ * <ul>
+ * <li>a wildcard at the beginning should match 1 or more folders</li>
+ * <li>any other wildcard must match exactly one folder</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Example:
+ * <pre>
+ * <excludePackageNames>*.internal:org.acme.exclude1.*:org.acme.exclude2</excludePackageNames>
+ * </pre>
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#exclude">exclude</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ * </p>
+ */
+ @Parameter( property = "excludePackageNames" )
+ private String excludePackageNames;
+
+ /**
+ * Specifies the directories where extension classes reside. Separate directories in <code>extdirs</code> with a
+ * colon (<code>:</code>) or a semi-colon (<code>;</code>).
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#extdirs">extdirs</a>.
+ */
+ @Parameter( property = "extdirs" )
+ private String extdirs;
+
+ /**
+ * Specifies the locale that javadoc uses when generating documentation.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#locale">locale</a>.
+ */
+ @Parameter( property = "locale" )
+ private String locale;
+
+ /**
+ * Specifies the maximum Java heap size to be used when launching the Javadoc tool.
+ * JVMs refer to this property as the <code>-Xmx</code> parameter. Example: '512' or '512m'.
+ * The memory unit depends on the JVM used. The units supported could be: <code>k</code>, <code>kb</code>,
+ * <code>m</code>, <code>mb</code>, <code>g</code>, <code>gb</code>, <code>t</code>, <code>tb</code>.
+ * If no unit specified, the default unit is <code>m</code>.
+ */
+ @Parameter( property = "maxmemory" )
+ private String maxmemory;
+
+ /**
+ * Specifies the minimum Java heap size to be used when launching the Javadoc tool.
+ * JVMs refer to this property as the <code>-Xms</code> parameter. Example: '512' or '512m'.
+ * The memory unit depends on the JVM used. The units supported could be: <code>k</code>, <code>kb</code>,
+ * <code>m</code>, <code>mb</code>, <code>g</code>, <code>gb</code>, <code>t</code>, <code>tb</code>.
+ * If no unit specified, the default unit is <code>m</code>.
+ */
+ @Parameter( property = "minmemory" )
+ private String minmemory;
+
+ /**
+ * This option creates documentation with the appearance and functionality of documentation generated by
+ * Javadoc 1.1.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#1.1">1.1</a>.
+ * <br/>
+ */
+ @Parameter( property = "old", defaultValue = "false" )
+ private boolean old;
+
+ /**
+ * Specifies that javadoc should retrieve the text for the overview documentation from the "source" file
+ * specified by path/filename and place it on the Overview page (overview-summary.html).
+ * <br/>
+ * <b>Note</b>: could be in conflict with <nooverview/>.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#overview">overview</a>.
+ * <br/>
+ */
+ @Parameter( property = "overview", defaultValue = "${basedir}/src/main/javadoc/overview.html" )
+ private File overview;
+
+ /**
+ * Shuts off non-error and non-warning messages, leaving only the warnings and errors appear, making them
+ * easier to view.
+ * <br/>
+ * Note: was a standard doclet in Java 1.4.2 (refer to bug ID
+ * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4714350">4714350</a>).
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#quiet">quiet</a>.
+ * <br/>
+ * Since Java 5.0.
+ * <br/>
+ */
+ @Parameter( property = "quiet", defaultValue = "false" )
+ private boolean quiet;
+
+ /**
+ * Specifies the access level for classes and members to show in the Javadocs.
+ * Possible values are:
+ * <ul>
+ * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#public">public</a>
+ * (shows only public classes and members)</li>
+ * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#protected">protected</a>
+ * (shows only public and protected classes and members)</li>
+ * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#package">package</a>
+ * (shows all classes and members not marked private)</li>
+ * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#private">private</a>
+ * (shows all classes and members)</li>
+ * </ul>
+ * <br/>
+ */
+ @Parameter( property = "show", defaultValue = "protected" )
+ private String show;
+
+ /**
+ * Necessary to enable javadoc to handle assertions introduced in J2SE v 1.4 source code or generics introduced in
+ * J2SE v5.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#source">source</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ */
+ @Parameter( property = "source" )
+ private String source;
+
+ /**
+ * Provide source compatibility with specified release
+ *
+ * @since JDK 9
+ * @since 3.1.0
+ */
+ @Parameter( defaultValue = "${maven.compiler.release}" )
+ private String release;
+
+ /**
+ * Specifies the source paths where the subpackages are located. The <code>sourcepath</code> can contain
+ * multiple paths by separating them with a colon (<code>:</code>) or a semi-colon (<code>;</code>).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#sourcepath">sourcepath</a>.
+ */
+ @Parameter( property = "sourcepath" )
+ private String sourcepath;
+
+ /**
+ * Specifies the package directory where javadoc will be executed. Multiple packages can be separated by
+ * colons (<code>:</code>).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#subpackages">subpackages</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ */
+ @Parameter( property = "subpackages" )
+ private String subpackages;
+
+ /**
+ * Provides more detailed messages while javadoc is running.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#verbose">verbose</a>.
+ * <br/>
+ */
+ @Parameter( property = "verbose", defaultValue = "false" )
+ private boolean verbose;
+
+ // ----------------------------------------------------------------------
+ // Standard Doclet Options - all alphabetical
+ // ----------------------------------------------------------------------
+
+ /**
+ * Specifies whether or not the author text is included in the generated Javadocs.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#author">author</a>.
+ * <br/>
+ */
+ @Parameter( property = "author", defaultValue = "true" )
+ private boolean author;
+
+ /**
+ * Specifies the text to be placed at the bottom of each output file.<br/>
+ * If you want to use html you have to put it in a CDATA section, <br/>
+ * eg. <code><![CDATA[Copyright 2005, <a href="http://www.mycompany.com">MyCompany, Inc.<a>]]></code>
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#bottom">bottom</a>.
+ * <br/>
+ */
+ @Parameter( property = "bottom",
+ defaultValue = "Copyright © {inceptionYear}–{currentYear} {organizationName}. "
+ + "All rights reserved." )
+ private String bottom;
+
+ /**
+ * Specifies the HTML character set for this document. If not specificed, the charset value will be the value of
+ * the <code>docencoding</code> parameter.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#charset">charset</a>.
+ * <br/>
+ */
+ @Parameter( property = "charset" )
+ private String charset;
+
+ /**
+ * Specifies the encoding of the generated HTML files. If not specificed, the docencoding value will be
+ * <code>UTF-8</code>.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docencoding">docencoding</a>.
+ */
+ @Parameter( property = "docencoding", defaultValue = "${project.reporting.outputEncoding}" )
+ private String docencoding;
+
+ /**
+ * Enables deep copying of the <code>**/doc-files</code> directories and the specifc <code>resources</code>
+ * directory from the <code>javadocDirectory</code> directory (for instance,
+ * <code>src/main/javadoc/com/mycompany/myapp/doc-files</code> and <code>src/main/javadoc/resources</code>).
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#docfilessubdirs">
+ * docfilessubdirs</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ * <br/>
+ * See <a href="#javadocDirectory">javadocDirectory</a>.
+ * <br/>
+ *
+ * @see #excludedocfilessubdir
+ * @see #javadocDirectory
+ */
+ @Parameter( property = "docfilessubdirs", defaultValue = "false" )
+ private boolean docfilessubdirs;
+
+ /**
+ * Specifies specific checks to be performed on Javadoc comments.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#BEJEFABE">doclint</a>.
+ *
+ * @since 3.0.0
+ */
+ @Parameter( property = "doclint" )
+ private String doclint;
+
+ /**
+ * Specifies the title to be placed near the top of the overview summary file.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#doctitle">doctitle</a>.
+ * <br/>
+ */
+ @Parameter( property = "doctitle", defaultValue = "${project.name} ${project.version} API" )
+ private String doctitle;
+
+ /**
+ * Excludes any "doc-files" subdirectories with the given names. Multiple patterns can be excluded
+ * by separating them with colons (<code>:</code>).
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#excludedocfilessubdir">
+ * excludedocfilessubdir</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ *
+ * @see #docfilessubdirs
+ */
+ @Parameter( property = "excludedocfilessubdir" )
+ private String excludedocfilessubdir;
+
+ /**
+ * Specifies the footer text to be placed at the bottom of each output file.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#footer">footer</a>.
+ */
+ @Parameter( property = "footer" )
+ private String footer;
+
+ /**
+ * Separates packages on the overview page into whatever groups you specify, one group per table. The
+ * packages pattern can be any package name, or can be the start of any package name followed by an asterisk
+ * (<code>*</code>) meaning "match any characters". Multiple patterns can be included in a group
+ * by separating them with colons (<code>:</code>).
+ * <br/>
+ * Example:
+ * <pre>
+ * <groups>
+ * <group>
+ * <title>Core Packages</title>
+ * <!-- To includes java.lang, java.lang.ref,
+ * java.lang.reflect and only java.util
+ * (i.e. not java.util.jar) -->
+ * <packages>java.lang*:java.util</packages>
+ * </group>
+ * <group>
+ * <title>Extension Packages</title>
+ * <!-- To include javax.accessibility,
+ * javax.crypto, ... (among others) -->
+ * <packages>javax.*</packages>
+ * </group>
+ * </groups>
+ * </pre>
+ * <b>Note</b>: using <code>java.lang.*</code> for <code>packages</code> would omit the <code>java.lang</code>
+ * package but using <code>java.lang*</code> will include it.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#group">group</a>.
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/Group.html">Javadoc</a>.
+ * <br/>
+ */
+ @Parameter
+ private Group[] groups;
+
+ /**
+ * Specifies the header text to be placed at the top of each output file.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#header">header</a>.
+ */
+ @Parameter( property = "header" )
+ private String header;
+
+ /**
+ * Specifies the path of an alternate help file path\filename that the HELP link in the top and bottom
+ * navigation bars link to.
+ * <br/>
+ * <b>Note</b>: could be in conflict with <nohelp/>.
+ * <br/>
+ * The <code>helpfile</code> could be an absolute File path.
+ * <br/>
+ * Since 2.6, it could be also be a path from a resource in the current project source directories
+ * (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
+ * or from a resource in the Javadoc plugin dependencies, for instance:
+ * <pre>
+ * <helpfile>path/to/your/resource/yourhelp-doc.html</helpfile>
+ * </pre>
+ * Where <code>path/to/your/resource/yourhelp-doc.html</code> could be in <code>src/main/javadoc</code>.
+ * <pre>
+ * <build>
+ * <plugins>
+ * <plugin>
+ * <groupId>org.apache.maven.plugins</groupId>
+ * <artifactId>maven-javadoc-plugin</artifactId>
+ * <configuration>
+ * <helpfile>path/to/your/resource/yourhelp-doc.html</helpfile>
+ * ...
+ * </configuration>
+ * <dependencies>
+ * <dependency>
+ * <groupId>groupId</groupId>
+ * <artifactId>artifactId</artifactId>
+ * <version>version</version>
+ * </dependency>
+ * </dependencies>
+ * </plugin>
+ * ...
+ * <plugins>
+ * </build>
+ * </pre>
+ * Where <code>path/to/your/resource/yourhelp-doc.html</code> is defined in the
+ * <code>groupId:artifactId:version</code> javadoc plugin dependency.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#helpfile">helpfile</a>.
+ */
+ @Parameter( property = "helpfile" )
+ private String helpfile;
+
+ /**
+ * Adds HTML meta keyword tags to the generated file for each class.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#keywords">keywords</a>.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
+ * Java 1.4.2</a>.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.5.0.html#commandlineoptions">
+ * Java 5.0</a>.
+ * <br/>
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "keywords", defaultValue = "false" )
+ private boolean keywords;
+
+ /**
+ * Creates links to existing javadoc-generated documentation of external referenced classes.
+ * <br>
+ * <b>Notes</b>:
+ * <ol>
+ * <li>only used if {@link #isOffline} is set to <code>false</code>.</li>
+ * <li>all given links should have a fetchable <code>/package-list</code> file. For instance:
+ * <pre>
+ * <links>
+ * <link>http://docs.oracle.com/javase/1.4.2/docs/api</link>
+ * <links>
+ * </pre>
+ * will be used because <code>http://docs.oracle.com/javase/1.4.2/docs/api/package-list</code> exists.</li>
+ * <li>if {@link #detectLinks} is defined, the links between the project dependencies are
+ * automatically added.</li>
+ * <li>if {@link #detectJavaApiLink} is defined, a Java API link, based on the Java version of the
+ * project's sources, will be added automatically.</li>
+ * </ol>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#link">link</a>.
+ *
+ * @see #detectLinks
+ * @see #detectJavaApiLink
+ */
+ @Parameter( property = "links" )
+ protected ArrayList<String> links;
+
+ /**
+ * Creates an HTML version of each source file (with line numbers) and adds links to them from the standard
+ * HTML documentation.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#linksource">linksource</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ * <br/>
+ */
+ @Parameter( property = "linksource", defaultValue = "false" )
+ private boolean linksource;
+
+ /**
+ * Suppress the entire comment body, including the main description and all tags, generating only declarations.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nocomment">nocomment</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ * <br/>
+ */
+ @Parameter( property = "nocomment", defaultValue = "false" )
+ private boolean nocomment;
+
+ /**
+ * Prevents the generation of any deprecated API at all in the documentation.
+ * <br/>
+ * See
+ * <a
+ * href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nodeprecated">nodeprecated</a>.
+ * <br/>
+ */
+ @Parameter( property = "nodeprecated", defaultValue = "false" )
+ private boolean nodeprecated;
+
+ /**
+ * Prevents the generation of the file containing the list of deprecated APIs (deprecated-list.html) and the
+ * link in the navigation bar to that page.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nodeprecatedlist">
+ * nodeprecatedlist</a>.
+ * <br/>
+ */
+ @Parameter( property = "nodeprecatedlist", defaultValue = "false" )
+ private boolean nodeprecatedlist;
+
+ /**
+ * Omits the HELP link in the navigation bars at the top and bottom of each page of output.
+ * <br/>
+ * <b>Note</b>: could be in conflict with <helpfile/>.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nohelp">nohelp</a>.
+ * <br/>
+ */
+ @Parameter( property = "nohelp", defaultValue = "false" )
+ private boolean nohelp;
+
+ /**
+ * Omits the index from the generated docs.
+ * <br/>
+ * <b>Note</b>: could be in conflict with <splitindex/>.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#noindex">noindex</a>.
+ * <br/>
+ */
+ @Parameter( property = "noindex", defaultValue = "false" )
+ private boolean noindex;
+
+ /**
+ * Omits the navigation bar from the generated docs.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nonavbar">nonavbar</a>.
+ * <br/>
+ */
+ @Parameter( property = "nonavbar", defaultValue = "false" )
+ private boolean nonavbar;
+
+ /**
+ * Omits the entire overview page from the generated docs.
+ * <br/>
+ * <b>Note</b>: could be in conflict with <overview/>.
+ * <br/>
+ * Standard Doclet undocumented option.
+ * <br/>
+ *
+ * @since 2.4
+ */
+ @Parameter( property = "nooverview", defaultValue = "false" )
+ private boolean nooverview;
+
+ /**
+ * Omits qualifying package name from ahead of class names in output.
+ * Example:
+ * <pre>
+ * <noqualifier>all</noqualifier>
+ * or
+ * <noqualifier>packagename1:packagename2</noqualifier>
+ * </pre>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#noqualifier">noqualifier</a>.
+ * <br/>
+ * Since <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java
+ * 1.4</a>.
+ */
+ @Parameter( property = "noqualifier" )
+ private String noqualifier;
+
+ /**
+ * Omits from the generated docs the "Since" sections associated with the since tags.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#nosince">nosince</a>.
+ * <br/>
+ */
+ @Parameter( property = "nosince", defaultValue = "false" )
+ private boolean nosince;
+
+ /**
+ * Suppresses the timestamp, which is hidden in an HTML comment in the generated HTML near the top of each page.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#notimestamp">notimestamp</a>.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.5.0.html#commandlineoptions">
+ * Java 5.0</a>.
+ * <br/>
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "notimestamp", defaultValue = "false" )
+ private boolean notimestamp;
+
+ /**
+ * Omits the class/interface hierarchy pages from the generated docs.
+ * <br>
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#notree">notree</a> option
+ */
+ @Parameter( property = "notree", defaultValue = "false" )
+ private boolean notree;
+
+ /**
+ * This option is a variation of <code>-link</code>; they both create links to javadoc-generated documentation
+ * for external referenced classes.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#linkoffline">linkoffline</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <offlineLinks>
+ * <offlineLink>
+ * <url>http://docs.oracle.com/javase/1.5.0/docs/api/</url>
+ * <location>../javadoc/jdk-5.0/</location>
+ * </offlineLink>
+ * </offlineLinks>
+ * </pre>
+ * <br/>
+ * <b>Note</b>: if {@link #detectOfflineLinks} is defined, the offline links between the project modules are
+ * automatically added if the goal is calling in a non-aggregator way.
+ * <br>
+ * @see <a href="./apidocs/org/apache/maven/plugin/javadoc/options/OfflineLink.html">Javadoc</a>.
+ */
+ @Parameter( property = "offlineLinks" )
+ private OfflineLink[] offlineLinks;
+
+ /**
+ * Specifies the destination directory where javadoc saves the generated HTML files.
+ * <br>
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#d">javadoc d</a>
+ */
+ @Parameter( property = "destDir", alias = "destDir", defaultValue = "${project.build.directory}/apidocs",
+ required = true )
+ protected File outputDirectory;
+
+ /**
+ * Specify the text for upper left frame.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
+ * Java 1.4.2</a>.
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "packagesheader" )
+ private String packagesheader;
+
+ /**
+ * Generates compile-time warnings for missing serial tags.
+ * <br/>
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#serialwarn">serialwarn</a> option
+ */
+ @Parameter( property = "serialwarn", defaultValue = "false" )
+ private boolean serialwarn;
+
+ /**
+ * Specify the number of spaces each tab takes up in the source. If no tab is used in source, the default
+ * space is used.
+ * <br/>
+ * Note: was <code>linksourcetab</code> in Java 1.4.2 (refer to bug ID
+ * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4788919">4788919</a>).
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.2.html#commandlineoptions">
+ * 1.4.2</a>.
+ * <br/>
+ * Since Java 5.0.
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "sourcetab", alias = "linksourcetab" )
+ private int sourcetab;
+
+ /**
+ * Splits the index file into multiple files, alphabetically, one file per letter, plus a file for any index
+ * entries that start with non-alphabetical characters.
+ * <br/>
+ * <b>Note</b>: could be in conflict with <noindex/>.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#splitindex">splitindex</a>.
+ * <br/>
+ */
+ @Parameter( property = "splitindex", defaultValue = "false" )
+ private boolean splitindex;
+
+ /**
+ * Specifies whether the stylesheet to be used is the <code>maven</code>'s javadoc stylesheet or
+ * <code>java</code>'s default stylesheet when a <i>stylesheetfile</i> parameter is not specified.
+ * <br/>
+ * Possible values: <code>maven<code> or <code>java</code>.
+ * <br/>
+ */
+ @Parameter( property = "stylesheet", defaultValue = "java" )
+ private String stylesheet;
+
+ /**
+ * Specifies the path of an alternate HTML stylesheet file.
+ * <br/>
+ * The <code>stylesheetfile</code> could be an absolute File path.
+ * <br/>
+ * Since 2.6, it could be also be a path from a resource in the current project source directories
+ * (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
+ * or from a resource in the Javadoc plugin dependencies, for instance:
+ * <pre>
+ * <stylesheetfile>path/to/your/resource/yourstylesheet.css</stylesheetfile>
+ * </pre>
+ * Where <code>path/to/your/resource/yourstylesheet.css</code> could be in <code>src/main/javadoc</code>.
+ * <pre>
+ * <build>
+ * <plugins>
+ * <plugin>
+ * <groupId>org.apache.maven.plugins</groupId>
+ * <artifactId>maven-javadoc-plugin</artifactId>
+ * <configuration>
+ * <stylesheetfile>path/to/your/resource/yourstylesheet.css</stylesheetfile>
+ * ...
+ * </configuration>
+ * <dependencies>
+ * <dependency>
+ * <groupId>groupId</groupId>
+ * <artifactId>artifactId</artifactId>
+ * <version>version</version>
+ * </dependency>
+ * </dependencies>
+ * </plugin>
+ * ...
+ * <plugins>
+ * </build>
+ * </pre>
+ * Where <code>path/to/your/resource/yourstylesheet.css</code> is defined in the
+ * <code>groupId:artifactId:version</code> javadoc plugin dependency.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#stylesheetfile">
+ * stylesheetfile</a>.
+ */
+ @Parameter( property = "stylesheetfile" )
+ private String stylesheetfile;
+
+ /**
+ * Specifies the class file that starts the taglet used in generating the documentation for that tag.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#taglet">taglet</a>.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java 1.4</a>.
+ */
+ @Parameter( property = "taglet" )
+ private String taglet;
+
+ /**
+ * Specifies the Taglet artifact containing the taglet class files (.class).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <taglets>
+ * <taglet>
+ * <tagletClass>com.sun.tools.doclets.ToDoTaglet</tagletClass>
+ * </taglet>
+ * <taglet>
+ * <tagletClass>package.to.AnotherTagletClass</tagletClass>
+ * </taglet>
+ * ...
+ * </taglets>
+ * <tagletArtifact>
+ * <groupId>group-Taglet</groupId>
+ * <artifactId>artifact-Taglet</artifactId>
+ * <version>version-Taglet</version>
+ * </tagletArtifact>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/TagletArtifact.html">Javadoc</a>.
+ * <br/>
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "tagletArtifact" )
+ private TagletArtifact tagletArtifact;
+
+ /**
+ * Specifies several Taglet artifacts containing the taglet class files (.class). These taglets class names will be
+ * auto-detect and so no need to specify them.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#taglet">taglet</a>.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <tagletArtifacts>
+ * <tagletArtifact>
+ * <groupId>group-Taglet</groupId>
+ * <artifactId>artifact-Taglet</artifactId>
+ * <version>version-Taglet</version>
+ * </tagletArtifact>
+ * ...
+ * </tagletArtifacts>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/TagletArtifact.html">Javadoc</a>.
+ * <br/>
+ *
+ * @since 2.5
+ */
+ @Parameter( property = "tagletArtifacts" )
+ private TagletArtifact[] tagletArtifacts;
+
+ /**
+ * Specifies the search paths for finding taglet class files (.class). The <code>tagletpath</code> can contain
+ * multiple paths by separating them with a colon (<code>:</code>) or a semi-colon (<code>;</code>).
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java 1.4</a>.
+ */
+ @Parameter( property = "tagletpath" )
+ private String tagletpath;
+
+ /**
+ * Enables the Javadoc tool to interpret multiple taglets.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#taglet">taglet</a>.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tagletpath">tagletpath</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <taglets>
+ * <taglet>
+ * <tagletClass>com.sun.tools.doclets.ToDoTaglet</tagletClass>
+ * <!--<tagletpath>/home/taglets</tagletpath>-->
+ * <tagletArtifact>
+ * <groupId>group-Taglet</groupId>
+ * <artifactId>artifact-Taglet</artifactId>
+ * <version>version-Taglet</version>
+ * </tagletArtifact>
+ * </taglet>
+ * </taglets>
+ * </pre>
+ * <br/>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/Taglet.html">Javadoc</a>.
+ * <br/>
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "taglets" )
+ private Taglet[] taglets;
+
+ /**
+ * Enables the Javadoc tool to interpret a simple, one-argument custom block tag tagname in doc comments.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#tag">tag</a>.
+ * <br/>
+ * Since
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#summary">Java 1.4</a>.
+ * <br/>
+ * Example:
+ * <pre>
+ * <tags>
+ * <tag>
+ * <name>todo</name>
+ * <placement>a</placement>
+ * <head>To Do:</head>
+ * </tag>
+ * </tags>
+ * </pre>
+ * <b>Note</b>: the placement should be a combinaison of Xaoptcmf letters:
+ * <ul>
+ * <li><b><code>X</code></b> (disable tag)</li>
+ * <li><b><code>a</code></b> (all)</li>
+ * <li><b><code>o</code></b> (overview)</li>
+ * <li><b><code>p</code></b> (packages)</li>
+ * <li><b><code>t</code></b> (types, that is classes and interfaces)</li>
+ * <li><b><code>c</code></b> (constructors)</li>
+ * <li><b><code>m</code></b> (methods)</li>
+ * <li><b><code>f</code></b> (fields)</li>
+ * </ul>
+ * See <a href="./apidocs/org/apache/maven/plugin/javadoc/options/Tag.html">Javadoc</a>.
+ * <br/>
+ */
+ @Parameter( property = "tags" )
+ private Tag[] tags;
+
+ /**
+ * Specifies the top text to be placed at the top of each output file.
+ * <br/>
+ * See <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6227616">6227616</a>.
+ * <br/>
+ * Since Java 6.0
+ *
+ * @since 2.4
+ */
+ @Parameter( property = "top" )
+ private String top;
+
+ /**
+ * Includes one "Use" page for each documented class and package.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#use">use</a>.
+ * <br/>
+ */
+ @Parameter( property = "use", defaultValue = "true" )
+ private boolean use;
+
+ /**
+ * Includes the version text in the generated docs.
+ * <br/>
+ * See <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#version">version</a>.
+ * <br/>
+ */
+ @Parameter( property = "version", defaultValue = "true" )
+ private boolean version;
+
+ /**
+ * Specifies the title to be placed in the HTML title tag.
+ * <br/>
+ * See
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#windowtitle">windowtitle</a>.
+ * <br/>
+ */
+ @Parameter( property = "windowtitle", defaultValue = "${project.name} ${project.version} API" )
+ private String windowtitle;
+
+ /**
+ * Whether dependency -sources jars should be resolved and included as source paths for javadoc generation.
+ * This is useful when creating javadocs for a distribution project.
+ *
+ * @since 2.7
+ */
+ @Parameter( defaultValue = "false" )
+ private boolean includeDependencySources;
+
+ /**
+ * Directory where unpacked project sources / test-sources should be cached.
+ *
+ * @see #includeDependencySources
+ * @since 2.7
+ */
+ @Parameter( defaultValue = "${project.build.directory}/distro-javadoc-sources" )
+ private File sourceDependencyCacheDir;
+
+ /**
+ * Whether to include transitive dependencies in the list of dependency -sources jars to include in this javadoc
+ * run.
+ *
+ * @see #includeDependencySources
+ * @since 2.7
+ * @deprecated if these sources depend on transitive dependencies, those dependencies should be added to the pom as
+ * direct dependencies
+ */
+ @Parameter( defaultValue = "false" )
+ @Deprecated
+ private boolean includeTransitiveDependencySources;
+
+ /**
+ * List of included dependency-source patterns. Example: <code>org.apache.maven:*</code>
+ *
+ * @see #includeDependencySources
+ * @since 2.7
+ */
+ @Parameter
+ private List<String> dependencySourceIncludes;
+
+ /**
+ * List of excluded dependency-source patterns. Example: <code>org.apache.maven.shared:*</code>
+ *
+ * @see #includeDependencySources
+ * @since 2.7
+ */
+ @Parameter
+ private List<String> dependencySourceExcludes;
+
+ /**
+ * Directory into which assembled {@link JavadocOptions} instances will be written before they
+ * are added to javadoc resources bundles.
+ *
+ * @since 2.7
+ */
+ @Parameter( defaultValue = "${project.build.directory}/javadoc-bundle-options", readonly = true )
+ private File javadocOptionsDir;
+
+ /**
+ * Transient variable to allow lazy-resolution of javadoc bundles from dependencies, so they can
+ * be used at various points in the javadoc generation process.
+ *
+ * @since 2.7
+ */
+ private transient List<JavadocBundle> dependencyJavadocBundles;
+
+ /**
+ * Capability to add additional dependencies to the javadoc classpath.
+ * Example:
+ * <pre>
+ * <additionalDependencies>
+ * <additionalDependency>
+ * <groupId>geronimo-spec</groupId>
+ * <artifactId>geronimo-spec-jta</artifactId>
+ * <version>1.0.1B-rc4</version>
+ * </additionalDependency>
+ * </additionalDependencies>
+ * </pre>
+ *
+ * @since 2.8.1
+ */
+ @Parameter
+ private List<AdditionalDependency> additionalDependencies;
+
+ /**
+ * Include filters on the source files. Default is **\/\*.java.
+ * These are ignored if you specify subpackages or subpackage excludes.
+ *
+ * @since 2.9
+ */
+ @Parameter
+ private List<String> sourceFileIncludes;
+
+ /**
+ * exclude filters on the source files.
+ * These are ignored if you specify subpackages or subpackage excludes.
+ *
+ * @since 2.9
+ */
+ @Parameter
+ private List<String> sourceFileExcludes;
+
+ /**
+ * To apply the security fix on generated javadoc see http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-1571
+ * @since 2.9.1
+ */
+ @Parameter( defaultValue = "true", property = "maven.javadoc.applyJavadocSecurityFix" )
+ private boolean applyJavadocSecurityFix = true;
+
+ /**
+ * <p>
+ * Specify the requirements for this jdk toolchain.
+ * This overrules the toolchain selected by the maven-toolchain-plugin.
+ * </p>
+ * <strong>note:</strong> requires at least Maven 3.3.1
+ *
+ * @since 3.0.0
+ */
+ @Parameter
+ private Map<String, String> jdkToolchain;
+
+ /**
+ * <p>
+ * Location of the file used to store the state of the previous javadoc run.
+ * This is used to skip the generation if nothing has changed.
+ * </p>
+ *
+ * @since 3.2.0
+ */
+ @Parameter( property = "staleDataPath",
+ defaultValue = "${project.build.directory}/maven-javadoc-plugin-stale-data.txt" )
+ private File staleDataPath;
+
+ // ----------------------------------------------------------------------
+ // protected methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Indicates whether this goal is flagged with <code>@aggregator</code>.
+ *
+ * @return <code>true</code> if the goal is designed as an aggregator, <code>false</code> otherwise.
+ * @see AggregatorJavadocReport
+ * @see AggregatorTestJavadocReport
+ */
+ protected boolean isAggregator()
+ {
+ return false;
+ }
+
+ /**
+ * Indicates whether this goal generates documentation for the <code>Java Test code</code>.
+ *
+ * @return <code>true</code> if the goal generates Test Javadocs, <code>false</code> otherwise.
+ */
+ protected boolean isTest()
+ {
+ return false;
+ }
+
+ /**
+ * @return the output directory
+ */
+ protected String getOutputDirectory()
+ {
+ return outputDirectory.getAbsoluteFile().toString();
+ }
+
+ protected MavenProject getProject()
+ {
+ return project;
+ }
+
+ /**
+ * @param p not null maven project
+ * @return the list of directories where compiled classes are placed for the given project. These dirs are
+ * added in the javadoc classpath.
+ */
+ protected List<File> getProjectBuildOutputDirs( MavenProject p )
+ {
+ if ( StringUtils.isEmpty( p.getBuild().getOutputDirectory() ) )
+ {
+ return Collections.emptyList();
+ }
+
+ return Collections.singletonList( new File( p.getBuild().getOutputDirectory() ) );
+ }
+
+ protected File getArtifactFile( MavenProject project )
+ {
+ if ( !isAggregator() && isTest() )
+ {
+ return null;
+ }
+ else if ( project.getArtifact() != null )
+ {
+ return project.getArtifact().getFile();
+ }
+ return null;
+ }
+
+ /**
+ * @param p not null maven project
+ * @return the list of source paths for the given project
+ */
+ protected List<String> getProjectSourceRoots( MavenProject p )
+ {
+ if ( "pom".equals( p.getPackaging().toLowerCase() ) )
+ {
+ return Collections.emptyList();
+ }
+
+ return ( p.getCompileSourceRoots() == null
+ ? Collections.<String>emptyList()
+ : new LinkedList<>( p.getCompileSourceRoots() ) );
+ }
+
+ /**
+ * @param p not null maven project
+ * @return the list of source paths for the execution project of the given project
+ */
+ protected List<String> getExecutionProjectSourceRoots( MavenProject p )
+ {
+ if ( "pom".equals( p.getExecutionProject().getPackaging().toLowerCase() ) )
+ {
+ return Collections.emptyList();
+ }
+
+ return ( p.getExecutionProject().getCompileSourceRoots() == null
+ ? Collections.<String>emptyList()
+ : new LinkedList<>( p.getExecutionProject().getCompileSourceRoots() ) );
+ }
+
+ /**
+ * @return the current javadoc directory
+ */
+ protected File getJavadocDirectory()
+ {
+ return javadocDirectory;
+ }
+
+ /**
+ * @return the doclint specific checks configuration
+ */
+ protected String getDoclint()
+ {
+ return doclint;
+ }
+
+ /**
+ * @return the title to be placed near the top of the overview summary file
+ */
+ protected String getDoctitle()
+ {
+ return doctitle;
+ }
+
+ /**
+ * @return the overview documentation file from the user parameter or from the <code>javadocdirectory</code>
+ */
+ protected File getOverview()
+ {
+ return overview;
+ }
+
+ /**
+ * @return the title to be placed in the HTML title tag
+ */
+ protected String getWindowtitle()
+ {
+ return windowtitle;
+ }
+
+ /**
+ * @return the charset attribute or the value of {@link #getDocencoding()} if <code>null</code>.
+ */
+ private String getCharset()
+ {
+ return ( StringUtils.isEmpty( charset ) ) ? getDocencoding() : charset;
+ }
+
+ /**
+ * @return the docencoding attribute or <code>UTF-8</code> if <code>null</code>.
+ */
+ private String getDocencoding()
+ {
+ return ( StringUtils.isEmpty( docencoding ) ) ? ReaderFactory.UTF_8 : docencoding;
+ }
+
+ /**
+ * @return the encoding attribute or the value of <code>file.encoding</code> system property if <code>null</code>.
+ */
+ private String getEncoding()
+ {
+ return ( StringUtils.isEmpty( encoding ) ) ? ReaderFactory.FILE_ENCODING : encoding;
+ }
+
+ @Override
+ public void execute()
+ throws MojoExecutionException, MojoFailureException
+ {
+ verifyRemovedParameter( "aggregator" );
+ verifyRemovedParameter( "proxyHost" );
+ verifyRemovedParameter( "proxyPort" );
+ verifyReplacedParameter( "additionalparam", "additionalOptions" );
+
+ doExecute();
+ }
+
+ abstract void doExecute() throws MojoExecutionException, MojoFailureException;
+
+ protected final void verifyRemovedParameter( String paramName )
+ {
+ Xpp3Dom configDom = mojo.getConfiguration();
+ if ( configDom != null )
+ {
+ if ( configDom.getChild( paramName ) != null )
+ {
+ throw new IllegalArgumentException( "parameter '" + paramName
+ + "' has been removed from the plugin, please verify documentation." );
+ }
+ }
+ }
+
+ private void verifyReplacedParameter( String oldParamName, String newParamNew )
+ {
+ Xpp3Dom configDom = mojo.getConfiguration();
+ if ( configDom != null )
+ {
+ if ( configDom.getChild( oldParamName ) != null )
+ {
+ throw new IllegalArgumentException( "parameter '" + oldParamName
+ + "' has been replaced with " + newParamNew + ", please verify documentation." );
+ }
+ }
+ }
+
+ /**
+ * The <a href="package-summary.html">package documentation</a> details the
+ * Javadoc Options used by this Plugin.
+ *
+ * @param unusedLocale the wanted locale (actually unused).
+ * @throws MavenReportException if any
+ */
+ protected void executeReport( Locale unusedLocale )
+ throws MavenReportException
+ {
+ if ( skip )
+ {
+ getLog().info( "Skipping javadoc generation" );
+ return;
+ }
+
+ if ( getLog().isDebugEnabled() )
+ {
+ this.debug = true;
+ }
+
+ // NOTE: Always generate this file, to allow javadocs from modules to be aggregated via
+ // useDependencySources in a distro module build.
+ try
+ {
+ buildJavadocOptions();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Failed to generate javadoc options file: " + e.getMessage(), e );
+ }
+
+ Map<String, Collection<Path>> sourcePaths = getSourcePaths();
+
+ Collection<Path> collectedSourcePaths = collect( sourcePaths.values() );
+
+ Map<Path, Collection<String>> files = getFiles( collectedSourcePaths );
+ if ( !canGenerateReport( files ) )
+ {
+ return;
+ }
+
+ // ----------------------------------------------------------------------
+ // Find the javadoc executable and version
+ // ----------------------------------------------------------------------
+
+ String jExecutable;
+ try
+ {
+ jExecutable = getJavadocExecutable();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Unable to find javadoc command: " + e.getMessage(), e );
+ }
+ setFJavadocVersion( new File( jExecutable ) );
+
+ List<String> packageNames;
+ if ( javadocRuntimeVersion.isAtLeast( "9" ) )
+ {
+ packageNames = getPackageNamesRespectingJavaModules( sourcePaths );
+ }
+ else
+ {
+ packageNames = getPackageNames( files );
+ }
+
+ // ----------------------------------------------------------------------
+ // Javadoc output directory as File
+ // ----------------------------------------------------------------------
+
+ File javadocOutputDirectory = new File( getOutputDirectory() );
+ if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.isDirectory() )
+ {
+ throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not a directory." );
+ }
+ if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.canWrite() )
+ {
+ throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not writable." );
+ }
+ javadocOutputDirectory.mkdirs();
+
+ // ----------------------------------------------------------------------
+ // Copy all resources
+ // ----------------------------------------------------------------------
+
+ copyAllResources( javadocOutputDirectory );
+
+ // ----------------------------------------------------------------------
+ // Create command line for Javadoc
+ // ----------------------------------------------------------------------
+
+ Commandline cmd = new Commandline();
+ cmd.getShell().setQuotedArgumentsEnabled( false ); // for Javadoc JVM args
+ cmd.setWorkingDirectory( javadocOutputDirectory.getAbsolutePath() );
+ cmd.setExecutable( jExecutable );
+
+ // ----------------------------------------------------------------------
+ // Wrap Javadoc JVM args
+ // ----------------------------------------------------------------------
+
+ addMemoryArg( cmd, "-Xmx", this.maxmemory );
+ addMemoryArg( cmd, "-Xms", this.minmemory );
+ addProxyArg( cmd );
+
+ if ( StringUtils.isNotEmpty( additionalJOption ) )
+ {
+ cmd.createArg().setValue( additionalJOption );
+ }
+
+ if ( additionalJOptions != null && additionalJOptions.length != 0 )
+ {
+ for ( String jo : additionalJOptions )
+ {
+ cmd.createArg().setValue( jo );
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Wrap Standard doclet Options
+ // ----------------------------------------------------------------------
+ List<String> standardDocletArguments = new ArrayList<>();
+
+ Set<OfflineLink> offlineLinks;
+ if ( StringUtils.isEmpty( doclet ) || useStandardDocletOptions )
+ {
+ offlineLinks = getLinkofflines();
+ addStandardDocletOptions( javadocOutputDirectory, standardDocletArguments, offlineLinks );
+ }
+ else
+ {
+ offlineLinks = Collections.emptySet();
+ }
+
+ // ----------------------------------------------------------------------
+ // Wrap Javadoc options
+ // ----------------------------------------------------------------------
+ List<String> javadocArguments = new ArrayList<>();
+
+ addJavadocOptions( javadocOutputDirectory, javadocArguments, sourcePaths, offlineLinks );
+
+ // ----------------------------------------------------------------------
+ // Write options file and include it in the command line
+ // ----------------------------------------------------------------------
+
+ List<String> arguments = new ArrayList<>( javadocArguments.size() + standardDocletArguments.size() );
+ arguments.addAll( javadocArguments );
+ arguments.addAll( standardDocletArguments );
+
+ if ( arguments.size() > 0 )
+ {
+ addCommandLineOptions( cmd, arguments, javadocOutputDirectory );
+ }
+
+ // ----------------------------------------------------------------------
+ // Write packages file and include it in the command line
+ // ----------------------------------------------------------------------
+
+ // MJAVADOC-365 if includes/excludes are specified, these take precedence over the default
+ // package-based mode and force javadoc into file-based mode unless subpackages are
+ // specified. Subpackages take precedence over file-based include/excludes. Why? Because
+ // getFiles(...) returns an empty list when subpackages are specified.
+ boolean includesExcludesActive =
+ ( sourceFileIncludes != null && !sourceFileIncludes.isEmpty() )
+ || ( sourceFileExcludes != null && !sourceFileExcludes.isEmpty() );
+ if ( includesExcludesActive && !StringUtils.isEmpty( subpackages ) )
+ {
+ getLog().warn( "sourceFileIncludes and sourceFileExcludes have no effect when subpackages are specified!" );
+ includesExcludesActive = false;
+ }
+ if ( !packageNames.isEmpty() && !includesExcludesActive )
+ {
+ addCommandLinePackages( cmd, javadocOutputDirectory, packageNames );
+
+ // ----------------------------------------------------------------------
+ // Write argfile file and include it in the command line
+ // ----------------------------------------------------------------------
+
+ List<String> specialFiles = getSpecialFiles( files );
+
+ if ( !specialFiles.isEmpty() )
+ {
+ addCommandLineArgFile( cmd, javadocOutputDirectory, specialFiles );
+ }
+ }
+ else
+ {
+ // ----------------------------------------------------------------------
+ // Write argfile file and include it in the command line
+ // ----------------------------------------------------------------------
+
+ List<String> allFiles = new ArrayList<>();
+ for ( Map.Entry<Path, Collection<String>> filesEntry : files.entrySet() )
+ {
+ for ( String file : filesEntry.getValue() )
+ {
+ allFiles.add( filesEntry.getKey().resolve( file ).toString() );
+ }
+ }
+
+ if ( !files.isEmpty() )
+ {
+ addCommandLineArgFile( cmd, javadocOutputDirectory, allFiles );
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Execute command line
+ // ----------------------------------------------------------------------
+
+ executeJavadocCommandLine( cmd, javadocOutputDirectory );
+
+ // delete generated javadoc files only if no error and no debug mode
+ // [MJAVADOC-336] Use File.delete() instead of File.deleteOnExit() to
+ // prevent these files from making their way into archives.
+ if ( !debug )
+ {
+ for ( int i = 0; i < cmd.getArguments().length; i++ )
+ {
+ String arg = cmd.getArguments()[i].trim();
+
+ if ( !arg.startsWith( "@" ) )
+ {
+ continue;
+ }
+
+ File argFile = new File( javadocOutputDirectory, arg.substring( 1 ) );
+ if ( argFile.exists() )
+ {
+ argFile.delete();
+ }
+ }
+
+ File scriptFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
+ if ( scriptFile.exists() )
+ {
+ scriptFile.delete();
+ }
+ }
+ if ( applyJavadocSecurityFix )
+ {
+ // finally, patch the Javadoc vulnerability in older Javadoc tools (CVE-2013-1571):
+ try
+ {
+ final int patched = fixFrameInjectionBug( javadocOutputDirectory, getDocencoding() );
+ if ( patched > 0 )
+ {
+ getLog().info(
+ String.format( "Fixed Javadoc frame injection vulnerability (CVE-2013-1571) in %d files.",
+ patched ) );
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Failed to patch javadocs vulnerability: " + e.getMessage(), e );
+ }
+ }
+ else
+ {
+ getLog().info( "applying javadoc security fix has been disabled" );
+ }
+ }
+
+ protected final <T> Collection<T> collect( Collection<Collection<T>> sourcePaths )
+ {
+ Collection<T> collectedSourcePaths = new LinkedHashSet<>();
+ for ( Collection<T> sp : sourcePaths )
+ {
+ collectedSourcePaths.addAll( sp );
+ }
+ return collectedSourcePaths;
+ }
+
+ /**
+ * Method to get the files on the specified source paths
+ *
+ * @param sourcePaths a Collection that contains the paths to the source files
+ * @return a List that contains the specific path for every source file
+ * @throws MavenReportException {@link MavenReportException}
+ */
+ protected Map<Path, Collection<String>> getFiles( Collection<Path> sourcePaths )
+ throws MavenReportException
+ {
+ Map<Path, Collection<String>> mappedFiles = new LinkedHashMap<>( sourcePaths.size() );
+ if ( StringUtils.isEmpty( subpackages ) )
+ {
+ Collection<String> excludedPackages = getExcludedPackages();
+
+ for ( Path sourcePath : sourcePaths )
+ {
+ List<String> files = new ArrayList<>();
+ File sourceDirectory = sourcePath.toFile();
+ files.addAll( JavadocUtil.getFilesFromSource( sourceDirectory, sourceFileIncludes, sourceFileExcludes,
+ excludedPackages ) );
+
+ if ( source != null && JavaVersion.parse( source ).isBefore( "9" )
+ && files.remove( "module-info.java" ) )
+ {
+ getLog().debug( "Auto exclude module-info.java due to source value" );
+ }
+ mappedFiles.put( sourcePath, files );
+ }
+ }
+
+ return mappedFiles;
+ }
+
+ /**
+ * Method to get the source paths per reactorProject. If no source path is specified in the parameter, the compile
+ * source roots of the project will be used.
+ *
+ * @return a Map of the project absolute source paths per projects key (G:A)
+ * @throws MavenReportException {@link MavenReportException}
+ * @see JavadocUtil#pruneDirs(MavenProject, Collection)
+ */
+ protected Map<String, Collection<Path>> getSourcePaths()
+ throws MavenReportException
+ {
+ Map<String, Collection<Path>> mappedSourcePaths = new LinkedHashMap<>();
+
+ if ( StringUtils.isEmpty( sourcepath ) )
+ {
+ if ( !"pom".equals( project.getPackaging() ) )
+ {
+ Set<Path> sourcePaths =
+ new LinkedHashSet<>( JavadocUtil.pruneDirs( project, getProjectSourceRoots( project ) ) );
+
+ if ( project.getExecutionProject() != null )
+ {
+ sourcePaths.addAll( JavadocUtil.pruneDirs( project, getExecutionProjectSourceRoots( project ) ) );
+ }
+
+ /*
+ * Should be after the source path (i.e. -sourcepath '.../src/main/java;.../src/main/javadoc') and *not*
+ * the opposite. If not, the javadoc tool always copies doc files, even if -docfilessubdirs is not
+ * setted.
+ */
+ if ( getJavadocDirectory() != null )
+ {
+ File javadocDir = getJavadocDirectory();
+ if ( javadocDir.exists() && javadocDir.isDirectory() )
+ {
+ Collection<Path> l =
+ JavadocUtil.pruneDirs( project,
+ Collections.singletonList( getJavadocDirectory().getAbsolutePath() ) );
+ sourcePaths.addAll( l );
+ }
+ }
+ if ( !sourcePaths.isEmpty() )
+ {
+ mappedSourcePaths.put( ArtifactUtils.versionlessKey( project.getGroupId(),
+ project.getArtifactId() ),
+ sourcePaths );
+ }
+ }
+
+ if ( includeDependencySources )
+ {
+ mappedSourcePaths.putAll( getDependencySourcePaths() );
+ }
+
+ if ( isAggregator() )
+ {
+ for ( MavenProject subProject : getAggregatedProjects() )
+ {
+ if ( subProject != project )
+ {
+ Collection<Path> additionalSourcePaths = new ArrayList<>();
+
+ List<String> sourceRoots = getProjectSourceRoots( subProject );
+
+ if ( subProject.getExecutionProject() != null )
+ {
+ sourceRoots.addAll( getExecutionProjectSourceRoots( subProject ) );
+ }
+
+ ArtifactHandler artifactHandler = subProject.getArtifact().getArtifactHandler();
+ if ( "java".equals( artifactHandler.getLanguage() ) )
+ {
+ additionalSourcePaths.addAll( JavadocUtil.pruneDirs( subProject, sourceRoots ) );
+ }
+
+ if ( getJavadocDirectory() != null )
+ {
+ String javadocDirRelative =
+ PathUtils.toRelative( project.getBasedir(),
+ getJavadocDirectory().getAbsolutePath() );
+ File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
+ if ( javadocDir.exists() && javadocDir.isDirectory() )
+ {
+ Collection<Path> l = JavadocUtil.pruneDirs( subProject, Collections.singletonList(
+ javadocDir.getAbsolutePath() ) );
+ additionalSourcePaths.addAll( l );
+ }
+ }
+ mappedSourcePaths.put( ArtifactUtils.versionlessKey( subProject.getGroupId(),
+ subProject.getArtifactId() ),
+ additionalSourcePaths );
+ }
+ }
+ }
+ }
+ else
+ {
+ Collection<Path> sourcePaths =
+ JavadocUtil.pruneDirs( project,
+ new ArrayList<>( Arrays.asList( JavadocUtil.splitPath( sourcepath ) ) ) );
+ if ( getJavadocDirectory() != null )
+ {
+ Collection<Path> l = JavadocUtil.pruneDirs( project, Collections.singletonList(
+ getJavadocDirectory().getAbsolutePath() ) );
+ sourcePaths.addAll( l );
+ }
+ mappedSourcePaths.put( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
+ sourcePaths );
+ }
+
+ return mappedSourcePaths;
+ }
+
+ private Collection<MavenProject> getAggregatedProjects()
+ {
+ Map<Path, MavenProject> reactorProjectsMap = new HashMap<>();
+ for ( MavenProject reactorProject : this.reactorProjects )
+ {
+ reactorProjectsMap.put( reactorProject.getBasedir().toPath(), reactorProject );
+ }
+
+ return modulesForAggregatedProject( project, reactorProjectsMap );
+ }
+
+ /**
+ * Recursively add the modules of the aggregatedProject to the set of aggregatedModules.
+ *
+ * @param aggregatedProject the project being aggregated
+ * @param reactorProjectsMap map of (still) available reactor projects
+ * @throws MavenReportException if any
+ */
+ private Set<MavenProject> modulesForAggregatedProject( MavenProject aggregatedProject,
+ Map<Path, MavenProject> reactorProjectsMap )
+ {
+ // Maven does not supply an easy way to get the projects representing
+ // the modules of a project. So we will get the paths to the base
+ // directories of the modules from the project and compare with the
+ // base directories of the projects in the reactor.
+
+ if ( aggregatedProject.getModules().isEmpty() )
+ {
+ return Collections.singleton( aggregatedProject );
+ }
+
+ List<Path> modulePaths = new LinkedList<>();
+ for ( String module : aggregatedProject.getModules() )
+ {
+ modulePaths.add( new File( aggregatedProject.getBasedir(), module ).toPath() );
+ }
+
+ Set<MavenProject> aggregatedModules = new LinkedHashSet<>();
+
+ for ( Path modulePath : modulePaths )
+ {
+ MavenProject module = reactorProjectsMap.remove( modulePath );
+ if ( module != null )
+ {
+ aggregatedModules.addAll( modulesForAggregatedProject( module, reactorProjectsMap ) );
+ }
+ }
+
+ return aggregatedModules;
+ }
+
+ /**
+ * Override this method to customize the configuration for resolving dependency sources. The default
+ * behavior enables the resolution of -sources jar files.
+ * @param config {@link SourceResolverConfig}
+ * @return {@link SourceResolverConfig}
+ */
+ protected SourceResolverConfig configureDependencySourceResolution( final SourceResolverConfig config )
+ {
+ return config.withCompileSources();
+ }
+
+ /**
+ * Resolve dependency sources so they can be included directly in the javadoc process. To customize this,
+ * override {@link AbstractJavadocMojo#configureDependencySourceResolution(SourceResolverConfig)}.
+ * @return List of source paths.
+ * @throws MavenReportException {@link MavenReportException}
+ */
+ protected final Map<String, Collection<Path>> getDependencySourcePaths()
+ throws MavenReportException
+ {
+ try
+ {
+ if ( sourceDependencyCacheDir.exists() )
+ {
+ FileUtils.forceDelete( sourceDependencyCacheDir );
+ sourceDependencyCacheDir.mkdirs();
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to delete cache directory: " + sourceDependencyCacheDir + "\nReason: " + e.getMessage(), e );
+ }
+
+ final SourceResolverConfig config = getDependencySourceResolverConfig();
+
+ try
+ {
+ return resourceResolver.resolveDependencySourcePaths( config );
+ }
+ catch ( final ArtifactResolutionException | ArtifactNotFoundException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e );
+ }
+ }
+
+ /**
+ * Returns a ArtifactFilter that only includes direct dependencies of this project
+ * (verified via groupId and artifactId).
+ *
+ * @return
+ */
+ private TransformableFilter createDependencyArtifactFilter()
+ {
+ Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
+
+ List<String> artifactPatterns = new ArrayList<>( dependencyArtifacts.size() );
+ for ( Artifact artifact : dependencyArtifacts )
+ {
+ artifactPatterns.add( artifact.getGroupId() + ":" + artifact.getArtifactId() );
+ }
+
+ return new PatternInclusionsFilter( artifactPatterns );
+ }
+
+ /**
+ * Construct a SourceResolverConfig for resolving dependency sources and resources in a consistent
+ * way, so it can be reused for both source and resource resolution.
+ *
+ * @since 2.7
+ */
+ private SourceResolverConfig getDependencySourceResolverConfig()
+ {
+ final List<TransformableFilter> andFilters = new ArrayList<>();
+
+ final List<String> dependencyIncludes = dependencySourceIncludes;
+ final List<String> dependencyExcludes = dependencySourceExcludes;
+
+ if ( !includeTransitiveDependencySources || isNotEmpty( dependencyIncludes ) || isNotEmpty(
+ dependencyExcludes ) )
+ {
+ if ( !includeTransitiveDependencySources )
+ {
+ andFilters.add( createDependencyArtifactFilter() );
+ }
+
+ if ( isNotEmpty( dependencyIncludes ) )
+ {
+ andFilters.add( new PatternInclusionsFilter( dependencyIncludes ) );
+ }
+
+ if ( isNotEmpty( dependencyExcludes ) )
+ {
+ andFilters.add( new PatternExclusionsFilter( dependencyExcludes ) );
+ }
+ }
+
+ return configureDependencySourceResolution( new SourceResolverConfig( project,
+ getProjectBuildingRequest( project ),
+ sourceDependencyCacheDir ).withReactorProjects( reactorProjects ) )
+ .withFilter( new AndFilter( andFilters ) );
+
+ }
+
+ private ProjectBuildingRequest getProjectBuildingRequest( MavenProject currentProject )
+ {
+ return new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() )
+ .setRemoteRepositories( currentProject.getRemoteArtifactRepositories() );
+ }
+
+ /**
+ * Method that indicates whether the javadoc can be generated or not. If the project does not contain any source
+ * files and no subpackages are specified, the plugin will terminate.
+ *
+ * @param files the project files
+ * @return a boolean that indicates whether javadoc report can be generated or not
+ */
+ protected boolean canGenerateReport( Map<Path, Collection<String>> files )
+ {
+ for ( Collection<String> filesValues : files.values() )
+ {
+ if ( !filesValues.isEmpty() )
+ {
+ return true;
+ }
+ }
+
+ return !StringUtils.isEmpty( subpackages );
+ }
+
+ // ----------------------------------------------------------------------
+ // private methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Method to get the excluded source files from the javadoc and create the argument string
+ * that will be included in the javadoc commandline execution.
+ *
+ * @param sourceFolders the collection of paths to the source files
+ * @return a String that contains the exclude argument that will be used by javadoc
+ * @throws MavenReportException
+ */
+ private String getExcludedPackages( Collection<Path> sourcePaths )
+ throws MavenReportException
+ {
+ List<String> excludedNames = null;
+
+ if ( StringUtils.isNotEmpty( sourcepath ) && StringUtils.isNotEmpty( subpackages ) )
+ {
+ Collection<String> excludedPackages = getExcludedPackages();
+
+ excludedNames = JavadocUtil.getExcludedPackages( sourcePaths, excludedPackages );
+ }
+
+ String excludeArg = "";
+ if ( StringUtils.isNotEmpty( subpackages ) && excludedNames != null )
+ {
+ // add the excludedpackage names
+ excludeArg = StringUtils.join( excludedNames.iterator(), ":" );
+ }
+
+ return excludeArg;
+ }
+
+ /**
+ * Method to format the specified source paths that will be accepted by the javadoc tool.
+ *
+ * @param sourcePaths the list of paths to the source files that will be included in the javadoc.
+ * @return a String that contains the formatted source path argument, separated by the System pathSeparator
+ * string (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
+ * @see File#pathSeparator
+ */
+ private String getSourcePath( Collection<Path> sourcePaths )
+ {
+ String sourcePath = null;
+
+ if ( StringUtils.isEmpty( subpackages ) || StringUtils.isNotEmpty( sourcepath ) )
+ {
+ sourcePath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
+ }
+
+ return sourcePath;
+ }
+
+ /**
+ * Method to get the packages specified in the <code>excludePackageNames</code> parameter. The packages are split
+ * with ',', ':', or ';' and then formatted.
+ *
+ * @return an array of String objects that contain the package names
+ * @throws MavenReportException
+ */
+ private Collection<String> getExcludedPackages()
+ throws MavenReportException
+ {
+ Set<String> excluded = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getExcludePackageNames() ) )
+ {
+ excluded.addAll( options.getExcludePackageNames() );
+ }
+ }
+ }
+ }
+
+ // for the specified excludePackageNames
+ if ( StringUtils.isNotEmpty( excludePackageNames ) )
+ {
+ List<String> packageNames = Arrays.asList( excludePackageNames.split( "[,:;]" ) );
+ excluded.addAll( trimValues( packageNames ) );
+ }
+
+ return excluded;
+ }
+
+ private static List<String> trimValues( List<String> items )
+ {
+ List<String> result = new ArrayList<>( items.size() );
+ for ( String item : items )
+ {
+ String trimmed = item.trim();
+ if ( StringUtils.isEmpty( trimmed ) )
+ {
+ continue;
+ }
+ result.add( trimmed );
+ }
+ return result;
+ }
+
+ /**
+ * Method that gets the classpath and modulepath elements that will be specified in the javadoc
+ * <code>-classpath</code> and <code>--module-path</code> parameter.
+ * Since we have all the sources of the current reactor, it is sufficient to consider the
+ * dependencies of the reactor modules, excluding the module artifacts which may not yet be available
+ * when the reactor project is built for the first time.
+ *
+ * @return all classpath elements
+ * @throws MavenReportException if any.
+ */
+ private Collection<File> getPathElements()
+ throws MavenReportException
+ {
+ Set<File> classpathElements = new LinkedHashSet<>();
+ Map<String, Artifact> compileArtifactMap = new LinkedHashMap<>();
+
+ if ( isTest() )
+ {
+ classpathElements.addAll( getProjectBuildOutputDirs( project ) );
+ }
+
+ populateCompileArtifactMap( compileArtifactMap, project.getArtifacts() );
+
+ if ( isAggregator() )
+ {
+ Collection<MavenProject> aggregatorProjects = getAggregatedProjects();
+
+ List<String> reactorArtifacts = new ArrayList<>();
+ for ( MavenProject p : aggregatorProjects )
+ {
+ reactorArtifacts.add( p.getGroupId() + ':' + p.getArtifactId() );
+ }
+
+ TransformableFilter dependencyFilter = new AndFilter( Arrays.asList(
+ new PatternExclusionsFilter( reactorArtifacts ),
+ getDependencyScopeFilter() ) );
+
+ for ( MavenProject subProject : aggregatorProjects )
+ {
+ if ( subProject != project )
+ {
+ File projectArtifactFile = getArtifactFile( subProject );
+ if ( projectArtifactFile != null )
+ {
+ classpathElements.add( projectArtifactFile );
+ }
+ else
+ {
+ classpathElements.addAll( getProjectBuildOutputDirs( subProject ) );
+ }
+
+ try
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "Compiled artifacts for " );
+ sb.append( subProject.getGroupId() ).append( ":" );
+ sb.append( subProject.getArtifactId() ).append( ":" );
+ sb.append( subProject.getVersion() ).append( '\n' );
+
+ ProjectBuildingRequest buildingRequest = getProjectBuildingRequest( subProject );
+
+ List<Dependency> managedDependencies = null;
+ if ( subProject.getDependencyManagement() != null )
+ {
+ managedDependencies = subProject.getDependencyManagement().getDependencies();
+ }
+
+ for ( ArtifactResult artifactResult
+ : dependencyResolver.resolveDependencies( buildingRequest,
+ subProject.getDependencies(),
+ managedDependencies,
+ dependencyFilter ) )
+ {
+ populateCompileArtifactMap( compileArtifactMap,
+ Collections.singletonList( artifactResult.getArtifact() ) );
+
+ sb.append( artifactResult.getArtifact().getFile() ).append( '\n' );
+ }
+
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().debug( sb.toString() );
+ }
+
+ }
+ catch ( DependencyResolverException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ }
+ }
+
+ for ( Artifact a : compileArtifactMap.values() )
+ {
+ classpathElements.add( a.getFile() );
+ }
+
+ if ( additionalDependencies != null )
+ {
+ for ( Dependency dependency : additionalDependencies )
+ {
+ Artifact artifact = resolveDependency( dependency );
+ getLog().debug( "add additional artifact with path " + artifact.getFile() );
+ classpathElements.add( artifact.getFile() );
+ }
+ }
+
+ return classpathElements;
+ }
+
+ protected ScopeFilter getDependencyScopeFilter()
+ {
+ return ScopeFilter.including( Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_SYSTEM );
+ }
+
+ /**
+ * @param dependency {@link Dependency}
+ * @return {@link Artifact}
+ * @throws MavenReportException when artifact could not be resolved
+ */
+ public Artifact resolveDependency( Dependency dependency )
+ throws MavenReportException
+ {
+ DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
+ coordinate.setGroupId( dependency.getGroupId() );
+ coordinate.setArtifactId( dependency.getArtifactId() );
+ coordinate.setVersion( dependency.getVersion() );
+ coordinate.setClassifier( dependency.getClassifier() );
+ coordinate.setExtension( artifactHandlerManager.getArtifactHandler( dependency.getType() ).getExtension() );
+
+ try
+ {
+ return artifactResolver.resolveArtifact( getProjectBuildingRequest( project ), coordinate ).getArtifact();
+ }
+ catch ( ArtifactResolverException e )
+ {
+ throw new MavenReportException( "artifact resolver problem - " + e.getMessage(), e );
+ }
+ }
+
+
+ //TODO remove the part with ToolchainManager lookup once we depend on
+ //3.0.9 (have it as prerequisite). Define as regular component field then.
+ protected final Toolchain getToolchain()
+ {
+ Toolchain tc = null;
+
+ if ( jdkToolchain != null )
+ {
+ // Maven 3.3.1 has plugin execution scoped Toolchain Support
+ try
+ {
+ Method getToolchainsMethod =
+ toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, String.class,
+ Map.class );
+
+ @SuppressWarnings( "unchecked" )
+ List<Toolchain> tcs =
+ (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk",
+ jdkToolchain );
+
+ if ( tcs != null && tcs.size() > 0 )
+ {
+ tc = tcs.get( 0 );
+ }
+ }
+ catch ( SecurityException | ReflectiveOperationException e )
+ {
+ // ignore
+ }
+ }
+
+ if ( tc == null )
+ {
+ tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
+ }
+
+ return tc;
+ }
+
+ /**
+ * Method to put the artifacts in the hashmap.
+ *
+ * @param compileArtifactMap the hashmap that will contain the artifacts
+ * @param artifactList the list of artifacts that will be put in the map
+ * @throws MavenReportException if any
+ */
+ private void populateCompileArtifactMap( Map<String, Artifact> compileArtifactMap,
+ Collection<Artifact> artifactList )
+ throws MavenReportException
+ {
+ if ( artifactList == null )
+ {
+ return;
+ }
+
+ for ( Artifact newArtifact : artifactList )
+ {
+ File file = newArtifact.getFile();
+
+ if ( file == null )
+ {
+ throw new MavenReportException(
+ "Error in plugin descriptor - " + "dependency was not resolved for artifact: "
+ + newArtifact.getGroupId() + ":" + newArtifact.getArtifactId() + ":"
+ + newArtifact.getVersion() );
+ }
+
+ if ( compileArtifactMap.get( newArtifact.getDependencyConflictId() ) != null )
+ {
+ Artifact oldArtifact = compileArtifactMap.get( newArtifact.getDependencyConflictId() );
+
+ ArtifactVersion oldVersion = new DefaultArtifactVersion( oldArtifact.getVersion() );
+ ArtifactVersion newVersion = new DefaultArtifactVersion( newArtifact.getVersion() );
+ if ( newVersion.compareTo( oldVersion ) > 0 )
+ {
+ compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
+ }
+ }
+ else
+ {
+ compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
+ }
+ }
+ }
+
+ /**
+ * Method that sets the bottom text that will be displayed on the bottom of the
+ * javadocs.
+ *
+ * @return a String that contains the text that will be displayed at the bottom of the javadoc
+ */
+ private String getBottomText()
+ {
+ int currentYear = Calendar.getInstance().get( Calendar.YEAR );
+ String year = String.valueOf( currentYear );
+
+ String inceptionYear = project.getInceptionYear();
+
+ String theBottom = StringUtils.replace( this.bottom, "{currentYear}", year );
+
+ if ( inceptionYear != null )
+ {
+ if ( inceptionYear.equals( year ) )
+ {
+ theBottom = StringUtils.replace( theBottom, "{inceptionYear}–", "" );
+ }
+ else
+ {
+ theBottom = StringUtils.replace( theBottom, "{inceptionYear}", inceptionYear );
+ }
+ }
+ else
+ {
+ theBottom = StringUtils.replace( theBottom, "{inceptionYear}–", "" );
+ }
+
+ if ( project.getOrganization() == null )
+ {
+ theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
+ }
+ else
+ {
+ if ( StringUtils.isNotEmpty( project.getOrganization().getName() ) )
+ {
+ if ( StringUtils.isNotEmpty( project.getOrganization().getUrl() ) )
+ {
+ theBottom = StringUtils.replace( theBottom, "{organizationName}",
+ "<a href=\"" + project.getOrganization().getUrl() + "\">"
+ + project.getOrganization().getName() + "</a>" );
+ }
+ else
+ {
+ theBottom =
+ StringUtils.replace( theBottom, "{organizationName}", project.getOrganization().getName() );
+ }
+ }
+ else
+ {
+ theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
+ }
+ }
+
+ return theBottom;
+ }
+
+ /**
+ * Method to get the stylesheet path file to be used by the Javadoc Tool.
+ * <br/>
+ * If the {@link #stylesheetfile} is empty, return the file as String definded by {@link #stylesheet} value.
+ * <br/>
+ * If the {@link #stylesheetfile} is defined, return the file as String.
+ * <br/>
+ * Note: since 2.6, the {@link #stylesheetfile} could be a path from a resource in the project source
+ * directories (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
+ * or from a resource in the Javadoc plugin dependencies.
+ *
+ * @param javadocOutputDirectory the output directory
+ * @return the stylesheet file absolute path as String.
+ * @see #getResource(List, String)
+ */
+ private String getStylesheetFile( final File javadocOutputDirectory )
+ {
+ if ( StringUtils.isEmpty( stylesheetfile ) )
+ {
+ if ( "java".equalsIgnoreCase( stylesheet ) )
+ {
+ // use the default Javadoc tool stylesheet
+ return null;
+ }
+
+ // maven, see #copyDefaultStylesheet(File)
+ return new File( javadocOutputDirectory, DEFAULT_CSS_NAME ).getAbsolutePath();
+ }
+
+ if ( new File( stylesheetfile ).exists() )
+ {
+ return new File( stylesheetfile ).getAbsolutePath();
+ }
+
+ return getResource( new File( javadocOutputDirectory, DEFAULT_CSS_NAME ), stylesheetfile );
+ }
+
+ /**
+ * Method to get the help file to be used by the Javadoc Tool.
+ * <br/>
+ * Since 2.6, the {@link #helpfile} could be a path from a resource in the project source
+ * directories (i.e. <code>src/main/java</code>, <code>src/main/resources</code> or <code>src/main/javadoc</code>)
+ * or from a resource in the Javadoc plugin dependencies.
+ *
+ * @param javadocOutputDirectory the output directory.
+ * @return the help file absolute path as String.
+ * @see #getResource(File, String)
+ * @since 2.6
+ */
+ private String getHelpFile( final File javadocOutputDirectory )
+ {
+ if ( StringUtils.isEmpty( helpfile ) )
+ {
+ return null;
+ }
+
+ if ( new File( helpfile ).exists() )
+ {
+ return new File( helpfile ).getAbsolutePath();
+ }
+
+ return getResource( new File( javadocOutputDirectory, "help-doc.html" ), helpfile );
+ }
+
+ /**
+ * Method to get the access level for the classes and members to be shown in the generated javadoc.
+ * If the specified access level is not public, protected, package or private, the access level
+ * is set to protected.
+ *
+ * @return the access level
+ */
+ private String getAccessLevel()
+ {
+ String accessLevel;
+ if ( "public".equalsIgnoreCase( show ) || "protected".equalsIgnoreCase( show ) || "package".equalsIgnoreCase(
+ show ) || "private".equalsIgnoreCase( show ) )
+ {
+ accessLevel = "-" + show;
+ }
+ else
+ {
+ if ( getLog().isErrorEnabled() )
+ {
+ getLog().error( "Unrecognized access level to show '" + show + "'. Defaulting to protected." );
+ }
+ accessLevel = "-protected";
+ }
+
+ return accessLevel;
+ }
+
+ /**
+ * Method to get the path of the bootclass artifacts used in the <code>-bootclasspath</code> option.
+ *
+ * @return a string that contains bootclass path, separated by the System pathSeparator string
+ * (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
+ * @throws MavenReportException if any
+ * @see File#pathSeparator
+ */
+ private String getBootclassPath()
+ throws MavenReportException
+ {
+ Set<BootclasspathArtifact> bootclasspathArtifacts = collectBootClasspathArtifacts();
+
+ List<String> bootclassPath = new ArrayList<>();
+ for ( BootclasspathArtifact aBootclasspathArtifact : bootclasspathArtifacts )
+ {
+ if ( ( StringUtils.isNotEmpty( aBootclasspathArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
+ aBootclasspathArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
+ aBootclasspathArtifact.getVersion() ) ) )
+ {
+ bootclassPath.addAll( getArtifactsAbsolutePath( aBootclasspathArtifact ) );
+ }
+ }
+
+ bootclassPath = JavadocUtil.pruneFiles( bootclassPath );
+
+ StringBuilder path = new StringBuilder();
+ path.append( StringUtils.join( bootclassPath.iterator(), File.pathSeparator ) );
+
+ if ( StringUtils.isNotEmpty( bootclasspath ) )
+ {
+ path.append( JavadocUtil.unifyPathSeparator( bootclasspath ) );
+ }
+
+ return path.toString();
+ }
+
+ /**
+ * Method to get the path of the doclet artifacts used in the <code>-docletpath</code> option.
+ * <p/>
+ * Either docletArtifact or doclectArtifacts can be defined and used, not both, docletArtifact
+ * takes precedence over doclectArtifacts. docletPath is always appended to any result path
+ * definition.
+ *
+ * @return a string that contains doclet path, separated by the System pathSeparator string
+ * (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
+ * @throws MavenReportException if any
+ * @see File#pathSeparator
+ */
+ private String getDocletPath()
+ throws MavenReportException
+ {
+ Set<DocletArtifact> docletArtifacts = collectDocletArtifacts();
+ List<String> pathParts = new ArrayList<>();
+
+ for ( DocletArtifact docletArtifact : docletArtifacts )
+ {
+ if ( !isDocletArtifactEmpty( docletArtifact ) )
+ {
+ pathParts.addAll( getArtifactsAbsolutePath( docletArtifact ) );
+ }
+ }
+
+ if ( !StringUtils.isEmpty( docletPath ) )
+ {
+ pathParts.add( JavadocUtil.unifyPathSeparator( docletPath ) );
+ }
+
+ String path = StringUtils.join( pathParts.iterator(), File.pathSeparator );
+
+ if ( StringUtils.isEmpty( path ) && getLog().isWarnEnabled() )
+ {
+ getLog().warn( "No docletpath option was found. Please review <docletpath/> or <docletArtifact/>"
+ + " or <doclets/>." );
+ }
+
+ return path;
+ }
+
+ /**
+ * Verify if a doclet artifact is empty or not
+ *
+ * @param aDocletArtifact could be null
+ * @return <code>true</code> if aDocletArtifact or the groupId/artifactId/version of the doclet artifact is null,
+ * <code>false</code> otherwise.
+ */
+ private boolean isDocletArtifactEmpty( DocletArtifact aDocletArtifact )
+ {
+ if ( aDocletArtifact == null )
+ {
+ return true;
+ }
+
+ return StringUtils.isEmpty( aDocletArtifact.getGroupId() ) && StringUtils.isEmpty(
+ aDocletArtifact.getArtifactId() ) && StringUtils.isEmpty( aDocletArtifact.getVersion() );
+ }
+
+ /**
+ * Method to get the path of the taglet artifacts used in the <code>-tagletpath</code> option.
+ *
+ * @return a string that contains taglet path, separated by the System pathSeparator string
+ * (colon (<code>:</code>) on Solaris or semi-colon (<code>;</code>) on Windows).
+ * @throws MavenReportException if any
+ * @see File#pathSeparator
+ */
+ private String getTagletPath()
+ throws MavenReportException
+ {
+ Set<TagletArtifact> tArtifacts = collectTagletArtifacts();
+ Collection<String> pathParts = new ArrayList<>();
+
+ for ( TagletArtifact tagletArtifact : tArtifacts )
+ {
+ if ( ( tagletArtifact != null ) && ( StringUtils.isNotEmpty( tagletArtifact.getGroupId() ) )
+ && ( StringUtils.isNotEmpty( tagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
+ tagletArtifact.getVersion() ) ) )
+ {
+ pathParts.addAll( getArtifactsAbsolutePath( tagletArtifact ) );
+ }
+ }
+
+ Set<Taglet> taglets = collectTaglets();
+ for ( Taglet taglet : taglets )
+ {
+ if ( taglet == null )
+ {
+ continue;
+ }
+
+ if ( ( taglet.getTagletArtifact() != null ) && ( StringUtils.isNotEmpty(
+ taglet.getTagletArtifact().getGroupId() ) ) && ( StringUtils.isNotEmpty(
+ taglet.getTagletArtifact().getArtifactId() ) ) && ( StringUtils.isNotEmpty(
+ taglet.getTagletArtifact().getVersion() ) ) )
+ {
+ pathParts.addAll( JavadocUtil.pruneFiles( getArtifactsAbsolutePath( taglet.getTagletArtifact() ) ) );
+ }
+ else if ( StringUtils.isNotEmpty( taglet.getTagletpath() ) )
+ {
+ for ( Path dir : JavadocUtil.pruneDirs( project, Collections.singletonList( taglet.getTagletpath() ) ) )
+ {
+ pathParts.add( dir.toString() );
+ }
+ }
+ }
+
+ StringBuilder path = new StringBuilder();
+ path.append( StringUtils.join( pathParts.iterator(), File.pathSeparator ) );
+
+ if ( StringUtils.isNotEmpty( tagletpath ) )
+ {
+ path.append( JavadocUtil.unifyPathSeparator( tagletpath ) );
+ }
+
+ return path.toString();
+ }
+
+ private Set<String> collectLinks()
+ throws MavenReportException
+ {
+ Set<String> links = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getLinks() ) )
+ {
+ links.addAll( options.getLinks() );
+ }
+ }
+ }
+ }
+
+ if ( isNotEmpty( this.links ) )
+ {
+ links.addAll( this.links );
+ }
+
+ links.addAll( getDependenciesLinks() );
+
+ return followLinks( links );
+ }
+
+ private Set<Group> collectGroups()
+ throws MavenReportException
+ {
+ Set<Group> groups = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getGroups() ) )
+ {
+ groups.addAll( options.getGroups() );
+ }
+ }
+ }
+ }
+
+ if ( this.groups != null && this.groups.length > 0 )
+ {
+ groups.addAll( Arrays.asList( this.groups ) );
+ }
+
+ return groups;
+ }
+
+ private Set<ResourcesArtifact> collectResourcesArtifacts()
+ throws MavenReportException
+ {
+ Set<ResourcesArtifact> result = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getResourcesArtifacts() ) )
+ {
+ result.addAll( options.getResourcesArtifacts() );
+ }
+ }
+ }
+ }
+
+ if ( this.resourcesArtifacts != null && this.resourcesArtifacts.length > 0 )
+ {
+ result.addAll( Arrays.asList( this.resourcesArtifacts ) );
+ }
+
+ return result;
+ }
+
+ private Set<BootclasspathArtifact> collectBootClasspathArtifacts()
+ throws MavenReportException
+ {
+ Set<BootclasspathArtifact> result = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getBootclasspathArtifacts() ) )
+ {
+ result.addAll( options.getBootclasspathArtifacts() );
+ }
+ }
+ }
+ }
+
+ if ( this.bootclasspathArtifacts != null && this.bootclasspathArtifacts.length > 0 )
+ {
+ result.addAll( Arrays.asList( this.bootclasspathArtifacts ) );
+ }
+
+ return result;
+ }
+
+ private Set<OfflineLink> collectOfflineLinks()
+ throws MavenReportException
+ {
+ Set<OfflineLink> result = new LinkedHashSet<>();
+
+ OfflineLink javaApiLink = getDefaultJavadocApiLink();
+ if ( javaApiLink != null )
+ {
+ result.add( javaApiLink );
+ }
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getOfflineLinks() ) )
+ {
+ result.addAll( options.getOfflineLinks() );
+ }
+ }
+ }
+ }
+
+ if ( this.offlineLinks != null && this.offlineLinks.length > 0 )
+ {
+ result.addAll( Arrays.asList( this.offlineLinks ) );
+ }
+
+ return result;
+ }
+
+ private Set<Tag> collectTags()
+ throws MavenReportException
+ {
+ Set<Tag> tags = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getTags() ) )
+ {
+ tags.addAll( options.getTags() );
+ }
+ }
+ }
+ }
+
+ if ( this.tags != null && this.tags.length > 0 )
+ {
+ tags.addAll( Arrays.asList( this.tags ) );
+ }
+
+ return tags;
+ }
+
+ private Set<TagletArtifact> collectTagletArtifacts()
+ throws MavenReportException
+ {
+ Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
+ {
+ tArtifacts.addAll( options.getTagletArtifacts() );
+ }
+ }
+ }
+ }
+
+ if ( tagletArtifact != null )
+ {
+ tArtifacts.add( tagletArtifact );
+ }
+
+ if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
+ {
+ tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
+ }
+
+ return tArtifacts;
+ }
+
+ private Set<DocletArtifact> collectDocletArtifacts()
+ throws MavenReportException
+ {
+ Set<DocletArtifact> dArtifacts = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getDocletArtifacts() ) )
+ {
+ dArtifacts.addAll( options.getDocletArtifacts() );
+ }
+ }
+ }
+ }
+
+ if ( docletArtifact != null )
+ {
+ dArtifacts.add( docletArtifact );
+ }
+
+ if ( docletArtifacts != null && docletArtifacts.length > 0 )
+ {
+ dArtifacts.addAll( Arrays.asList( docletArtifacts ) );
+ }
+
+ return dArtifacts;
+ }
+
+ private Set<Taglet> collectTaglets()
+ throws MavenReportException
+ {
+ Set<Taglet> result = new LinkedHashSet<>();
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getTaglets() ) )
+ {
+ result.addAll( options.getTaglets() );
+ }
+ }
+ }
+ }
+
+ if ( taglets != null && taglets.length > 0 )
+ {
+ result.addAll( Arrays.asList( taglets ) );
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the Javadoc artifact path and its transitive dependencies path from the local repository
+ *
+ * @param javadocArtifact not null
+ * @return a list of locale artifacts absolute path
+ * @throws MavenReportException if any
+ */
+ private List<String> getArtifactsAbsolutePath( JavadocPathArtifact javadocArtifact )
+ throws MavenReportException
+ {
+ if ( ( StringUtils.isEmpty( javadocArtifact.getGroupId() ) ) && ( StringUtils.isEmpty(
+ javadocArtifact.getArtifactId() ) ) && ( StringUtils.isEmpty( javadocArtifact.getVersion() ) ) )
+ {
+ return Collections.emptyList();
+ }
+
+ List<String> path = new ArrayList<>();
+
+ try
+ {
+ Artifact artifact = createAndResolveArtifact( javadocArtifact );
+ path.add( artifact.getFile().getAbsolutePath() );
+
+ DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate();
+ coordinate.setGroupId( javadocArtifact.getGroupId() );
+ coordinate.setArtifactId( javadocArtifact.getArtifactId() );
+ coordinate.setVersion( javadocArtifact.getVersion() );
+
+ Iterable<ArtifactResult> deps =
+ dependencyResolver.resolveDependencies( getProjectBuildingRequest( project ), coordinate,
+ ScopeFilter.including( "compile", "provided" ) );
+ for ( ArtifactResult a : deps )
+ {
+ path.add( a.getArtifact().getFile().getAbsolutePath() );
+ }
+
+ return path;
+ }
+ catch ( ArtifactResolverException e )
+ {
+ throw new MavenReportException( "Unable to resolve artifact:" + javadocArtifact, e );
+ }
+ catch ( DependencyResolverException e )
+ {
+ throw new MavenReportException( "Unable to resolve dependencies for:" + javadocArtifact, e );
+ }
+ }
+
+ /**
+ * creates an {@link Artifact} representing the configured {@link JavadocPathArtifact} and resolves it.
+ *
+ * @param javadocArtifact the {@link JavadocPathArtifact} to resolve
+ * @return a resolved {@link Artifact}
+ * @throws ArtifactResolverException
+ */
+ private Artifact createAndResolveArtifact( JavadocPathArtifact javadocArtifact )
+ throws ArtifactResolverException
+ {
+ DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
+ coordinate.setGroupId( javadocArtifact.getGroupId() );
+ coordinate.setArtifactId( javadocArtifact.getArtifactId() );
+ coordinate.setVersion( javadocArtifact.getVersion() );
+
+ return artifactResolver.resolveArtifact( getProjectBuildingRequest( project ), coordinate ).getArtifact();
+ }
+
+ /**
+ * Method that adds/sets the java memory parameters in the command line execution.
+ *
+ * @param cmd the command line execution object where the argument will be added
+ * @param arg the argument parameter name
+ * @param memory the JVM memory value to be set
+ * @see JavadocUtil#parseJavadocMemory(String)
+ */
+ private void addMemoryArg( Commandline cmd, String arg, String memory )
+ {
+ if ( StringUtils.isNotEmpty( memory ) )
+ {
+ try
+ {
+ cmd.createArg().setValue( "-J" + arg + JavadocUtil.parseJavadocMemory( memory ) );
+ }
+ catch ( IllegalArgumentException e )
+ {
+ if ( getLog().isErrorEnabled() )
+ {
+ getLog().error( "Malformed memory pattern for '" + arg + memory + "'. Ignore this option." );
+ }
+ }
+ }
+ }
+
+ /**
+ * Method that adds/sets the javadoc proxy parameters in the command line execution.
+ *
+ * @param cmd the command line execution object where the argument will be added
+ */
+ private void addProxyArg( Commandline cmd )
+ {
+ if ( settings == null || settings.getProxies().isEmpty() )
+ {
+ return;
+ }
+
+ Map<String, Proxy> activeProxies = new HashMap<>();
+
+ for ( Proxy proxy : settings.getProxies() )
+ {
+ if ( proxy.isActive() )
+ {
+ String protocol = proxy.getProtocol();
+
+ if ( !activeProxies.containsKey( protocol ) )
+ {
+ activeProxies.put( protocol, proxy );
+ }
+ }
+ }
+
+ if ( activeProxies.containsKey( "https" ) )
+ {
+ Proxy httpsProxy = activeProxies.get( "https" );
+ if ( StringUtils.isNotEmpty( httpsProxy.getHost() ) )
+ {
+ cmd.createArg().setValue( "-J-Dhttps.proxyHost=" + httpsProxy.getHost() );
+ cmd.createArg().setValue( "-J-Dhttps.proxyPort=" + httpsProxy.getPort() );
+
+ if ( StringUtils.isNotEmpty( httpsProxy.getNonProxyHosts() )
+ && ( !activeProxies.containsKey( "http" )
+ || StringUtils.isEmpty( activeProxies.get( "http" ).getNonProxyHosts() ) ) )
+ {
+ cmd.createArg().setValue( "-J-Dhttp.nonProxyHosts=\""
+ + httpsProxy.getNonProxyHosts().replace( "|", "^|" ) + "\"" );
+ }
+ }
+ }
+
+ if ( activeProxies.containsKey( "http" ) )
+ {
+ Proxy httpProxy = activeProxies.get( "http" );
+ if ( StringUtils.isNotEmpty( httpProxy.getHost() ) )
+ {
+ cmd.createArg().setValue( "-J-Dhttp.proxyHost=" + httpProxy.getHost() );
+ cmd.createArg().setValue( "-J-Dhttp.proxyPort=" + httpProxy.getPort() );
+
+ if ( !activeProxies.containsKey( "https" ) )
+ {
+ cmd.createArg().setValue( "-J-Dhttps.proxyHost=" + httpProxy.getHost() );
+ cmd.createArg().setValue( "-J-Dhttps.proxyPort=" + httpProxy.getPort() );
+ }
+
+ if ( StringUtils.isNotEmpty( httpProxy.getNonProxyHosts() ) )
+ {
+ cmd.createArg().setValue( "-J-Dhttp.nonProxyHosts=\""
+ + httpProxy.getNonProxyHosts().replace( "|", "^|" ) + "\"" );
+ }
+ }
+ }
+
+ // We bravely ignore FTP because no one (probably) uses FTP for Javadoc
+ }
+
+ /**
+ * Get the path of the Javadoc tool executable depending the user entry or try to find it depending the OS
+ * or the <code>java.home</code> system property or the <code>JAVA_HOME</code> environment variable.
+ *
+ * @return the path of the Javadoc tool
+ * @throws IOException if not found
+ */
+ private String getJavadocExecutable()
+ throws IOException
+ {
+ Toolchain tc = getToolchain();
+
+ if ( tc != null )
+ {
+ getLog().info( "Toolchain in maven-javadoc-plugin: " + tc );
+ if ( javadocExecutable != null )
+ {
+ getLog().warn( "Toolchains are ignored, 'javadocExecutable' parameter is set to " + javadocExecutable );
+ }
+ else
+ {
+ javadocExecutable = tc.findTool( "javadoc" );
+ }
+ }
+
+ String javadocCommand = "javadoc" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
+
+ File javadocExe;
+
+ // ----------------------------------------------------------------------
+ // The javadoc executable is defined by the user
+ // ----------------------------------------------------------------------
+ if ( StringUtils.isNotEmpty( javadocExecutable ) )
+ {
+ javadocExe = new File( javadocExecutable );
+
+ if ( javadocExe.isDirectory() )
+ {
+ javadocExe = new File( javadocExe, javadocCommand );
+ }
+
+ if ( SystemUtils.IS_OS_WINDOWS && javadocExe.getName().indexOf( '.' ) < 0 )
+ {
+ javadocExe = new File( javadocExe.getPath() + ".exe" );
+ }
+
+ if ( !javadocExe.isFile() )
+ {
+ throw new IOException( "The javadoc executable '" + javadocExe
+ + "' doesn't exist or is not a file. Verify the <javadocExecutable/> parameter." );
+ }
+
+ return javadocExe.getAbsolutePath();
+ }
+
+ // ----------------------------------------------------------------------
+ // Try to find javadocExe from System.getProperty( "java.home" )
+ // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME
+ // should be in the JDK_HOME
+ // ----------------------------------------------------------------------
+ // For IBM's JDK 1.2
+ if ( SystemUtils.IS_OS_AIX )
+ {
+ javadocExe =
+ new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", javadocCommand );
+ }
+ // For Apple's JDK 1.6.x (and older?) on Mac OSX
+ // CHECKSTYLE_OFF: MagicNumber
+ else if ( SystemUtils.IS_OS_MAC_OSX && !JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast( "1.7" ) )
+ // CHECKSTYLE_ON: MagicNumber
+ {
+ javadocExe = new File( SystemUtils.getJavaHome() + File.separator + "bin", javadocCommand );
+ }
+ else
+ {
+ javadocExe =
+ new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", javadocCommand );
+ }
+
+ // ----------------------------------------------------------------------
+ // Try to find javadocExe from JAVA_HOME environment variable
+ // ----------------------------------------------------------------------
+ if ( !javadocExe.exists() || !javadocExe.isFile() )
+ {
+ Properties env = CommandLineUtils.getSystemEnvVars();
+ String javaHome = env.getProperty( "JAVA_HOME" );
+ if ( StringUtils.isEmpty( javaHome ) )
+ {
+ throw new IOException( "The environment variable JAVA_HOME is not correctly set." );
+ }
+ if ( ( !new File( javaHome ).getCanonicalFile().exists() )
+ || ( new File( javaHome ).getCanonicalFile().isFile() ) )
+ {
+ throw new IOException( "The environment variable JAVA_HOME=" + javaHome
+ + " doesn't exist or is not a valid directory." );
+ }
+
+ javadocExe = new File( javaHome + File.separator + "bin", javadocCommand );
+ }
+
+ if ( !javadocExe.getCanonicalFile().exists() || !javadocExe.getCanonicalFile().isFile() )
+ {
+ throw new IOException( "The javadoc executable '" + javadocExe
+ + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." );
+ }
+
+ return javadocExe.getAbsolutePath();
+ }
+
+ /**
+ * Set a new value for <code>javadocRuntimeVersion</code>
+ *
+ * @param jExecutable not null
+ * @throws MavenReportException if not found
+ * @see JavadocUtil#getJavadocVersion(File)
+ */
+ private void setFJavadocVersion( File jExecutable )
+ throws MavenReportException
+ {
+ JavaVersion jVersion;
+ try
+ {
+ jVersion = JavadocUtil.getJavadocVersion( jExecutable );
+ }
+ catch ( IOException | CommandLineException | IllegalArgumentException e )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Unable to find the javadoc version: " + e.getMessage() );
+ getLog().warn( "Using the Java version instead of, i.e. " + JAVA_VERSION );
+ }
+ jVersion = JAVA_VERSION;
+ }
+
+ if ( StringUtils.isNotEmpty( javadocVersion ) )
+ {
+ try
+ {
+ javadocRuntimeVersion = JavaVersion.parse( javadocVersion );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new MavenReportException( "Unable to parse javadoc version: " + e.getMessage(), e );
+ }
+
+ if ( javadocRuntimeVersion.compareTo( jVersion ) != 0 && getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Are you sure about the <javadocVersion/> parameter? It seems to be " + jVersion );
+ }
+ }
+ else
+ {
+ javadocRuntimeVersion = jVersion;
+ }
+ }
+
+ /**
+ * Is the Javadoc version at least the requested version.
+ *
+ * @param requiredVersion the required version, for example 1.5f
+ * @return <code>true</code> if the javadoc version is equal or greater than the
+ * required version
+ */
+ private boolean isJavaDocVersionAtLeast( JavaVersion requiredVersion )
+ {
+ return JAVA_VERSION.compareTo( requiredVersion ) >= 0;
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * conditionally based on the given flag.
+ *
+ * @param arguments a list of arguments, not null
+ * @param b the flag which controls if the argument is added or not.
+ * @param value the argument value to be added.
+ */
+ private void addArgIf( List<String> arguments, boolean b, String value )
+ {
+ if ( b )
+ {
+ arguments.add( value );
+ }
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * regarding the requested Java version.
+ *
+ * @param arguments a list of arguments, not null
+ * @param b the flag which controls if the argument is added or not.
+ * @param value the argument value to be added.
+ * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
+ * @see #addArgIf(List, boolean, String)
+ * @see #isJavaDocVersionAtLeast(JavaVersion)
+ */
+ private void addArgIf( List<String> arguments, boolean b, String value, JavaVersion requiredJavaVersion )
+ {
+ if ( b )
+ {
+ if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
+ {
+ addArgIf( arguments, true, value );
+ }
+ else
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( value + " option is not supported on Java version < " + requiredJavaVersion
+ + ". Ignore this option." );
+ }
+ }
+ }
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * if the the value is not null or empty.
+ * <p/>
+ * Moreover, the value could be comma separated.
+ *
+ * @param arguments a list of arguments, not null
+ * @param key the argument name.
+ * @param value the argument value to be added.
+ * @see #addArgIfNotEmpty(List, String, String, boolean)
+ */
+ private void addArgIfNotEmpty( List<String> arguments, String key, String value )
+ {
+ addArgIfNotEmpty( arguments, key, value, false );
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * if the the value is not null or empty.
+ * <p/>
+ * Moreover, the value could be comma separated.
+ *
+ * @param arguments a list of arguments, not null
+ * @param key the argument name.
+ * @param value the argument value to be added.
+ * @param repeatKey repeat or not the key in the command line
+ * @param splitValue if <code>true</code> given value will be tokenized by comma
+ * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
+ * @see #addArgIfNotEmpty(List, String, String, boolean, boolean)
+ * @see #isJavaDocVersionAtLeast(JavaVersion)
+ */
+ private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
+ boolean splitValue, JavaVersion requiredJavaVersion )
+ {
+ if ( StringUtils.isNotEmpty( value ) )
+ {
+ if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
+ {
+ addArgIfNotEmpty( arguments, key, value, repeatKey, splitValue );
+ }
+ else
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion
+ + ". Ignore this option." );
+ }
+ }
+ }
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * if the the value is not null or empty.
+ * <p/>
+ * Moreover, the value could be comma separated.
+ *
+ * @param arguments a list of arguments, not null
+ * @param key the argument name.
+ * @param value the argument value to be added.
+ * @param repeatKey repeat or not the key in the command line
+ * @param splitValue if <code>true</code> given value will be tokenized by comma
+ */
+ private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
+ boolean splitValue )
+ {
+ if ( StringUtils.isNotEmpty( value ) )
+ {
+ if ( StringUtils.isNotEmpty( key ) )
+ {
+ arguments.add( key );
+ }
+
+ if ( splitValue )
+ {
+ StringTokenizer token = new StringTokenizer( value, "," );
+ while ( token.hasMoreTokens() )
+ {
+ String current = token.nextToken().trim();
+
+ if ( StringUtils.isNotEmpty( current ) )
+ {
+ arguments.add( current );
+
+ if ( token.hasMoreTokens() && repeatKey )
+ {
+ arguments.add( key );
+ }
+ }
+ }
+ }
+ else
+ {
+ arguments.add( value );
+ }
+ }
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * if the the value is not null or empty.
+ * <p/>
+ * Moreover, the value could be comma separated.
+ *
+ * @param arguments a list of arguments, not null
+ * @param key the argument name.
+ * @param value the argument value to be added.
+ * @param repeatKey repeat or not the key in the command line
+ */
+ private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey )
+ {
+ addArgIfNotEmpty( arguments, key, value, repeatKey, true );
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * regarding the requested Java version.
+ *
+ * @param arguments a list of arguments, not null
+ * @param key the argument name.
+ * @param value the argument value to be added.
+ * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
+ * @see #addArgIfNotEmpty(List, String, String, JavaVersion, boolean)
+ */
+ private void addArgIfNotEmpty( List<String> arguments, String key, String value,
+ JavaVersion requiredJavaVersion )
+ {
+ addArgIfNotEmpty( arguments, key, value, requiredJavaVersion, false );
+ }
+
+ /**
+ * Convenience method to add an argument to the <code>command line</code>
+ * regarding the requested Java version.
+ *
+ * @param arguments a list of arguments, not null
+ * @param key the argument name.
+ * @param value the argument value to be added.
+ * @param requiredJavaVersion the required Java version, for example 1.31f or 1.4f
+ * @param repeatKey repeat or not the key in the command line
+ * @see #addArgIfNotEmpty(List, String, String)
+ * @see #isJavaDocVersionAtLeast
+ */
+ private void addArgIfNotEmpty( List<String> arguments, String key, String value, JavaVersion requiredJavaVersion,
+ boolean repeatKey )
+ {
+ if ( StringUtils.isNotEmpty( value ) )
+ {
+ if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
+ {
+ addArgIfNotEmpty( arguments, key, value, repeatKey );
+ }
+ else
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion );
+ }
+ }
+ }
+ }
+
+ /**
+ * Convenience method to process {@link #offlineLinks} values as individual <code>-linkoffline</code>
+ * javadoc options.
+ * <br/>
+ * If {@link #detectOfflineLinks}, try to add javadoc apidocs according Maven conventions for all modules given
+ * in the project.
+ *
+ * @param arguments a list of arguments, not null
+ * @throws MavenReportException if any
+ * @see #offlineLinks
+ * @see #getModulesLinks()
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#package-list">package-list spec</a>
+ */
+ private void addLinkofflineArguments( List<String> arguments, Set<OfflineLink> offlineLinksList )
+ throws MavenReportException
+ {
+ for ( OfflineLink offlineLink : offlineLinksList )
+ {
+ String url = offlineLink.getUrl();
+ if ( StringUtils.isEmpty( url ) )
+ {
+ continue;
+ }
+ url = cleanUrl( url );
+
+ String location = offlineLink.getLocation();
+ if ( StringUtils.isEmpty( location ) )
+ {
+ continue;
+ }
+ if ( isValidJavadocLink( location, false ) )
+ {
+ addArgIfNotEmpty( arguments, "-linkoffline",
+ JavadocUtil.quotedPathArgument( url ) + " " + JavadocUtil.quotedPathArgument(
+ location ), true );
+ }
+ }
+ }
+
+ private Set<OfflineLink> getLinkofflines() throws MavenReportException
+ {
+ Set<OfflineLink> offlineLinksList = collectOfflineLinks();
+
+ offlineLinksList.addAll( getModulesLinks() );
+
+ return offlineLinksList;
+ }
+
+ /**
+ * Convenience method to process {@link #links} values as individual <code>-link</code> javadoc options.
+ * If {@link #detectLinks}, try to add javadoc apidocs according Maven conventions for all dependencies given
+ * in the project.
+ * <br/>
+ * According the Javadoc documentation, all defined link should have <code>${link}/package-list</code> fetchable.
+ * <br/>
+ * <b>Note</b>: when a link is not fetchable:
+ * <ul>
+ * <li>Javadoc 1.4 and less throw an exception</li>
+ * <li>Javadoc 1.5 and more display a warning</li>
+ * </ul>
+ *
+ * @param arguments a list of arguments, not null
+ * @throws MavenReportException
+ * @see #detectLinks
+ * @see #getDependenciesLinks()
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#package-list">package-list spec</a>
+ */
+ private void addLinkArguments( List<String> arguments )
+ throws MavenReportException
+ {
+ Set<String> links = collectLinks();
+
+ for ( String link : links )
+ {
+ if ( StringUtils.isEmpty( link ) )
+ {
+ continue;
+ }
+
+ if ( isOffline && !link.startsWith( "file:" ) )
+ {
+ continue;
+ }
+
+ while ( link.endsWith( "/" ) )
+ {
+ link = link.substring( 0, link.lastIndexOf( "/" ) );
+ }
+
+ addArgIfNotEmpty( arguments, "-link", JavadocUtil.quotedPathArgument( link ), true, false );
+ }
+ }
+
+ /**
+ * Coppy all resources to the output directory
+ *
+ * @param javadocOutputDirectory not null
+ * @throws MavenReportException if any
+ * @see #copyDefaultStylesheet(File)
+ * @see #copyJavadocResources(File)
+ * @see #copyAdditionalJavadocResources(File)
+ */
+ private void copyAllResources( File javadocOutputDirectory )
+ throws MavenReportException
+ {
+ // ----------------------------------------------------------------------
+ // Copy default resources
+ // ----------------------------------------------------------------------
+
+ try
+ {
+ copyDefaultStylesheet( javadocOutputDirectory );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Unable to copy default stylesheet: " + e.getMessage(), e );
+ }
+
+ // ----------------------------------------------------------------------
+ // Copy javadoc resources
+ // ----------------------------------------------------------------------
+
+ if ( docfilessubdirs )
+ {
+ /*
+ * Workaround since -docfilessubdirs doesn't seem to be used correctly by the javadoc tool
+ * (see other note about -sourcepath). Take care of the -excludedocfilessubdir option.
+ */
+ try
+ {
+ copyJavadocResources( javadocOutputDirectory );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Unable to copy javadoc resources: " + e.getMessage(), e );
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Copy additional javadoc resources in artifacts
+ // ----------------------------------------------------------------------
+
+ copyAdditionalJavadocResources( javadocOutputDirectory );
+ }
+
+ /**
+ * Copies the {@link #DEFAULT_CSS_NAME} css file from the current class
+ * loader to the <code>outputDirectory</code> only if {@link #stylesheetfile} is empty and
+ * {@link #stylesheet} is equals to <code>maven</code>.
+ *
+ * @param anOutputDirectory the output directory
+ * @throws java.io.IOException if any
+ * @see #DEFAULT_CSS_NAME
+ * @see JavadocUtil#copyResource(java.net.URL, java.io.File)
+ */
+ private void copyDefaultStylesheet( File anOutputDirectory )
+ throws IOException
+ {
+ if ( StringUtils.isNotEmpty( stylesheetfile ) )
+ {
+ return;
+ }
+
+ if ( !stylesheet.equalsIgnoreCase( "maven" ) )
+ {
+ return;
+ }
+
+ URL url = getClass().getClassLoader().getResource( RESOURCE_CSS_DIR + "/" + DEFAULT_CSS_NAME );
+ File outFile = new File( anOutputDirectory, DEFAULT_CSS_NAME );
+ JavadocUtil.copyResource( url, outFile );
+ }
+
+ /**
+ * Method that copy all <code>doc-files</code> directories from <code>javadocDirectory</code> of
+ * the current project or of the projects in the reactor to the <code>outputDirectory</code>.
+ *
+ * @param anOutputDirectory the output directory
+ * @throws java.io.IOException if any
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.2.html#docfiles">Reference
+ * Guide, Copies new "doc-files" directory for holding images and examples</a>
+ * @see #docfilessubdirs
+ */
+ private void copyJavadocResources( File anOutputDirectory )
+ throws IOException
+ {
+ if ( anOutputDirectory == null || !anOutputDirectory.exists() )
+ {
+ throw new IOException( "The outputDirectory " + anOutputDirectory + " doesn't exists." );
+ }
+
+ if ( includeDependencySources )
+ {
+ resolveDependencyBundles();
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ File dir = bundle.getResourcesDirectory();
+ JavadocOptions options = bundle.getOptions();
+ if ( dir != null && dir.isDirectory() )
+ {
+ JavadocUtil.copyJavadocResources( anOutputDirectory, dir, options == null
+ ? null
+ : options.getExcludedDocfilesSubdirs() );
+ }
+ }
+ }
+ }
+
+ if ( getJavadocDirectory() != null )
+ {
+ JavadocUtil.copyJavadocResources( anOutputDirectory, getJavadocDirectory(), excludedocfilessubdir );
+ }
+
+ if ( isAggregator() )
+ {
+ for ( MavenProject subProject : getAggregatedProjects() )
+ {
+ if ( subProject != project && getJavadocDirectory() != null )
+ {
+ String javadocDirRelative =
+ PathUtils.toRelative( project.getBasedir(), getJavadocDirectory().getAbsolutePath() );
+ File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
+ JavadocUtil.copyJavadocResources( anOutputDirectory, javadocDir, excludedocfilessubdir );
+ }
+ }
+ }
+ }
+
+ private synchronized void resolveDependencyBundles()
+ throws IOException
+ {
+ if ( dependencyJavadocBundles == null )
+ {
+ dependencyJavadocBundles =
+ resourceResolver.resolveDependencyJavadocBundles( getDependencySourceResolverConfig() );
+ if ( dependencyJavadocBundles == null )
+ {
+ dependencyJavadocBundles = new ArrayList<>();
+ }
+ }
+ }
+
+ /**
+ * Method that copy additional Javadoc resources from given artifacts.
+ *
+ * @param anOutputDirectory the output directory
+ * @throws MavenReportException if any
+ * @see #resourcesArtifacts
+ */
+ private void copyAdditionalJavadocResources( File anOutputDirectory )
+ throws MavenReportException
+ {
+ Set<ResourcesArtifact> resourcesArtifacts = collectResourcesArtifacts();
+ if ( isEmpty( resourcesArtifacts ) )
+ {
+ return;
+ }
+
+ UnArchiver unArchiver;
+ try
+ {
+ unArchiver = archiverManager.getUnArchiver( "jar" );
+ }
+ catch ( NoSuchArchiverException e )
+ {
+ throw new MavenReportException(
+ "Unable to extract resources artifact. " + "No archiver for 'jar' available.", e );
+ }
+
+ for ( ResourcesArtifact item : resourcesArtifacts )
+ {
+ Artifact artifact;
+ try
+ {
+ artifact = createAndResolveArtifact( item );
+ }
+ catch ( ArtifactResolverException e )
+ {
+ throw new MavenReportException( "Unable to resolve artifact:" + item, e );
+ }
+
+ unArchiver.setSourceFile( artifact.getFile() );
+ unArchiver.setDestDirectory( anOutputDirectory );
+ // remove the META-INF directory from resource artifact
+ IncludeExcludeFileSelector[] selectors =
+ new IncludeExcludeFileSelector[]{ new IncludeExcludeFileSelector() };
+ selectors[0].setExcludes( new String[]{ "META-INF/**" } );
+ unArchiver.setFileSelectors( selectors );
+
+ getLog().info( "Extracting contents of resources artifact: " + artifact.getArtifactId() );
+ try
+ {
+ unArchiver.extract();
+ }
+ catch ( ArchiverException e )
+ {
+ throw new MavenReportException(
+ "Extraction of resources failed. Artifact that failed was: " + artifact.getArtifactId(), e );
+ }
+ }
+ }
+
+ /**
+ * @param sourcePaths could be null
+ * @return the list of package names for files in the sourcePaths
+ */
+ private List<String> getPackageNames( Map<Path, Collection<String>> sourcePaths )
+ {
+ List<String> returnList = new ArrayList<>();
+
+ if ( !StringUtils.isEmpty( sourcepath ) )
+ {
+ return returnList;
+ }
+
+ for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
+ {
+ for ( String currentFile : currentPathEntry.getValue() )
+ {
+ /*
+ * Remove the miscellaneous files
+ * http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
+ */
+ if ( currentFile.contains( "doc-files" ) )
+ {
+ continue;
+ }
+
+ int lastIndexOfSeparator = currentFile.lastIndexOf( "/" );
+ if ( lastIndexOfSeparator != -1 )
+ {
+ String packagename = currentFile.substring( 0, lastIndexOfSeparator ).replace( '/', '.' );
+
+ if ( !returnList.contains( packagename ) )
+ {
+ returnList.add( packagename );
+ }
+ }
+ }
+ }
+
+ return returnList;
+ }
+
+ /**
+ * @param allSourcePaths not null, containing absolute and relative paths
+ * @return a list of exported package names for files in allSourcePaths
+ * @throws MavenReportException if any
+ * @see #getFiles
+ * @see #getSourcePaths()
+ */
+ private List<String> getPackageNamesRespectingJavaModules( Map<String, Collection<Path>> allSourcePaths )
+ throws MavenReportException
+ {
+ List<String> returnList = new ArrayList<>();
+
+ if ( !StringUtils.isEmpty( sourcepath ) )
+ {
+ return returnList;
+ }
+
+ for ( Collection<Path> artifactSourcePaths: allSourcePaths.values() )
+ {
+ Set<String> exportedPackages = new HashSet<>();
+ boolean exportAllPackages;
+ File mainDescriptor = findMainDescriptor( artifactSourcePaths );
+ if ( mainDescriptor != null && !isTest() )
+ {
+ ResolvePathsRequest<File> request =
+ ResolvePathsRequest.ofFiles( Collections.<File>emptyList() ).
+ setMainModuleDescriptor( mainDescriptor );
+
+ try
+ {
+ Set<JavaModuleDescriptor.JavaExports> exports = locationManager.resolvePaths( request ).
+ getMainModuleDescriptor().exports();
+ if ( exports.isEmpty() )
+ {
+ continue;
+ }
+ for ( JavaModuleDescriptor.JavaExports export : exports )
+ {
+ exportedPackages.add( export.source() );
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ exportAllPackages = false;
+ }
+ else
+ {
+ exportAllPackages = true;
+ }
+
+ for ( Map.Entry<Path, Collection<String>> currentPathEntry : getFiles( artifactSourcePaths ).entrySet() )
+ {
+ for ( String currentFile : currentPathEntry.getValue() )
+ {
+ /*
+ * Remove the miscellaneous files
+ * http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
+ */
+ if ( currentFile.contains( "doc-files" ) )
+ {
+ continue;
+ }
+
+ int lastIndexOfSeparator = currentFile.lastIndexOf( File.separatorChar );
+ if ( lastIndexOfSeparator != -1 )
+ {
+ String packagename =
+ currentFile.substring( 0, lastIndexOfSeparator ).replace( File.separatorChar, '.' );
+
+ if ( exportAllPackages || exportedPackages.contains( packagename ) )
+ {
+ returnList.add( packagename );
+ }
+ }
+ }
+ }
+ }
+
+ return returnList;
+ }
+
+ /**
+ * @param sourcePaths could be null
+ * @return a list files with unnamed package names for files in the sourcePaths
+ */
+ private List<String> getFilesWithUnnamedPackages( Map<Path, Collection<String>> sourcePaths )
+ {
+ List<String> returnList = new ArrayList<>();
+
+ if ( !StringUtils.isEmpty( sourcepath ) )
+ {
+ return returnList;
+ }
+
+ for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
+ {
+ Path currentSourcePath = currentPathEntry.getKey();
+
+ for ( String currentFile : currentPathEntry.getValue() )
+ {
+ /*
+ * Remove the miscellaneous files
+ * http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
+ */
+ if ( currentFile.contains( "doc-files" ) )
+ {
+ continue;
+ }
+
+ if ( currentFile.indexOf( File.separatorChar ) == -1 )
+ {
+ returnList.add( currentSourcePath.resolve( currentFile ).toAbsolutePath().toString() );
+ }
+ }
+ }
+
+ return returnList;
+ }
+
+ /**
+ * Either return only the module descriptor or all sourcefiles per sourcepath
+ * @param sourcePaths could be null
+ * @return a list of files
+ */
+ private List<String> getSpecialFiles( Map<Path, Collection<String>> sourcePaths )
+ {
+ if ( !StringUtils.isEmpty( sourcepath ) )
+ {
+ return new ArrayList<>();
+ }
+
+ boolean containsModuleDescriptor = false;
+ for ( Collection<String> sourcepathFiles : sourcePaths.values() )
+ {
+ containsModuleDescriptor = sourcepathFiles.contains( "module-info.java" );
+ if ( containsModuleDescriptor )
+ {
+ break;
+ }
+ }
+
+ if ( containsModuleDescriptor )
+ {
+ return getModuleSourcePathFiles( sourcePaths );
+ }
+ else
+ {
+ return getFilesWithUnnamedPackages( sourcePaths );
+ }
+ }
+
+ private List<String> getModuleSourcePathFiles( Map<Path, Collection<String>> sourcePaths )
+ {
+ List<String> returnList = new ArrayList<>();
+
+ for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
+ {
+ Path currentSourcePath = currentPathEntry.getKey();
+ if ( currentPathEntry.getValue().contains( "module-info.java" ) )
+ {
+ returnList.add( currentSourcePath.resolve( "module-info.java" ).toAbsolutePath().toString() );
+ }
+ else
+ {
+ for ( String currentFile : currentPathEntry.getValue() )
+ {
+ /*
+ * Remove the miscellaneous files
+ * http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
+ */
+ if ( currentFile.contains( "doc-files" ) )
+ {
+ continue;
+ }
+
+ returnList.add( currentSourcePath.resolve( currentFile ).toAbsolutePath().toString() );
+ }
+ }
+ }
+ return returnList;
+ }
+
+ /**
+ * Generate an <code>options</code> file for all options and arguments and add the <code>@options</code> in the
+ * command line.
+ *
+ * @param cmd not null
+ * @param arguments not null
+ * @param javadocOutputDirectory not null
+ * @throws MavenReportException if any
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#argumentfiles">
+ * Reference Guide, Command line argument files</a>
+ * @see #OPTIONS_FILE_NAME
+ */
+ private void addCommandLineOptions( Commandline cmd, List<String> arguments, File javadocOutputDirectory )
+ throws MavenReportException
+ {
+ File optionsFile = new File( javadocOutputDirectory, OPTIONS_FILE_NAME );
+
+ StringBuilder options = new StringBuilder();
+ options.append( StringUtils.join( arguments.iterator(),
+ SystemUtils.LINE_SEPARATOR ) );
+
+ /* default to platform encoding */
+ String outputFileEncoding = null;
+ if ( JAVA_VERSION.isAtLeast( "9" ) )
+ {
+ outputFileEncoding = StandardCharsets.UTF_8.name();
+ }
+ try
+ {
+ FileUtils.fileWrite( optionsFile.getAbsolutePath(), outputFileEncoding, options.toString() );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Unable to write '" + optionsFile.getName() + "' temporary file for command execution", e );
+ }
+
+ cmd.createArg().setValue( "@" + OPTIONS_FILE_NAME );
+ }
+
+ /**
+ * Generate a file called <code>argfile</code> (or <code>files</code>, depending the JDK) to hold files and add
+ * the <code>@argfile</code> (or <code>@file</code>, depending the JDK) in the command line.
+ *
+ * @param cmd not null
+ * @param javadocOutputDirectory not null
+ * @param files not null
+ * @throws MavenReportException if any
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#argumentfiles">
+ * Reference Guide, Command line argument files
+ * </a>
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.4.html#runningjavadoc">
+ * What s New in Javadoc 1.4
+ * </a>
+ * @see #isJavaDocVersionAtLeast(JavaVersion)
+ * @see #ARGFILE_FILE_NAME
+ * @see #FILES_FILE_NAME
+ */
+ private void addCommandLineArgFile( Commandline cmd, File javadocOutputDirectory, List<String> files )
+ throws MavenReportException
+ {
+ File argfileFile;
+ if ( JAVA_VERSION.compareTo( SINCE_JAVADOC_1_4 ) >= 0 )
+ {
+ argfileFile = new File( javadocOutputDirectory, ARGFILE_FILE_NAME );
+ cmd.createArg().setValue( "@" + ARGFILE_FILE_NAME );
+ }
+ else
+ {
+ argfileFile = new File( javadocOutputDirectory, FILES_FILE_NAME );
+ cmd.createArg().setValue( "@" + FILES_FILE_NAME );
+ }
+
+ List<String> quotedFiles = new ArrayList<>( files.size() );
+ for ( String file : files )
+ {
+ quotedFiles.add( JavadocUtil.quotedPathArgument( file ) );
+ }
+
+ try
+ {
+ FileUtils.fileWrite( argfileFile.getAbsolutePath(), null /* platform encoding */,
+ StringUtils.join( quotedFiles.iterator(), SystemUtils.LINE_SEPARATOR ) );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Unable to write '" + argfileFile.getName() + "' temporary file for command execution", e );
+ }
+ }
+
+ /**
+ * Generate a file called <code>packages</code> to hold all package names and add the <code>@packages</code> in
+ * the command line.
+ *
+ * @param cmd not null
+ * @param javadocOutputDirectory not null
+ * @param packageNames not null
+ * @throws MavenReportException if any
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#argumentfiles">
+ * Reference Guide, Command line argument files</a>
+ * @see #PACKAGES_FILE_NAME
+ */
+ private void addCommandLinePackages( Commandline cmd, File javadocOutputDirectory, List<String> packageNames )
+ throws MavenReportException
+ {
+ File packagesFile = new File( javadocOutputDirectory, PACKAGES_FILE_NAME );
+
+ try
+ {
+ FileUtils.fileWrite( packagesFile.getAbsolutePath(), null /* platform encoding */,
+ StringUtils.join( packageNames.iterator(), SystemUtils.LINE_SEPARATOR ) );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Unable to write '" + packagesFile.getName() + "' temporary file for command execution", e );
+ }
+
+ cmd.createArg().setValue( "@" + PACKAGES_FILE_NAME );
+ }
+
+ /**
+ * Checks for the validity of the Javadoc options used by the user.
+ *
+ * @throws MavenReportException if error
+ */
+ private void validateJavadocOptions()
+ throws MavenReportException
+ {
+ // encoding
+ if ( StringUtils.isNotEmpty( getEncoding() ) && !JavadocUtil.validateEncoding( getEncoding() ) )
+ {
+ throw new MavenReportException( "Unsupported option <encoding/> '" + getEncoding() + "'" );
+ }
+
+ // locale
+ if ( StringUtils.isNotEmpty( this.locale ) )
+ {
+ StringTokenizer tokenizer = new StringTokenizer( this.locale, "_" );
+ final int maxTokens = 3;
+ if ( tokenizer.countTokens() > maxTokens )
+ {
+ throw new MavenReportException(
+ "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
+ }
+
+ Locale localeObject = null;
+ if ( tokenizer.hasMoreTokens() )
+ {
+ String language = tokenizer.nextToken().toLowerCase( Locale.ENGLISH );
+ if ( !Arrays.asList( Locale.getISOLanguages() ).contains( language ) )
+ {
+ throw new MavenReportException(
+ "Unsupported language '" + language + "' in option <locale/> '" + this.locale + "'" );
+ }
+ localeObject = new Locale( language );
+
+ if ( tokenizer.hasMoreTokens() )
+ {
+ String country = tokenizer.nextToken().toUpperCase( Locale.ENGLISH );
+ if ( !Arrays.asList( Locale.getISOCountries() ).contains( country ) )
+ {
+ throw new MavenReportException(
+ "Unsupported country '" + country + "' in option <locale/> '" + this.locale + "'" );
+ }
+ localeObject = new Locale( language, country );
+
+ if ( tokenizer.hasMoreTokens() )
+ {
+ String variant = tokenizer.nextToken();
+ localeObject = new Locale( language, country, variant );
+ }
+ }
+ }
+
+ if ( localeObject == null )
+ {
+ throw new MavenReportException(
+ "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
+ }
+
+ this.locale = localeObject.toString();
+ final List<Locale> availableLocalesList = Arrays.asList( Locale.getAvailableLocales() );
+ if ( StringUtils.isNotEmpty( localeObject.getVariant() ) && !availableLocalesList.contains( localeObject ) )
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append( "Unsupported option <locale/> with variant '" ).append( this.locale );
+ sb.append( "'" );
+
+ localeObject = new Locale( localeObject.getLanguage(), localeObject.getCountry() );
+ this.locale = localeObject.toString();
+
+ sb.append( ", trying to use <locale/> without variant, i.e. '" ).append( this.locale ).append( "'" );
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( sb.toString() );
+ }
+ }
+
+ if ( !availableLocalesList.contains( localeObject ) )
+ {
+ throw new MavenReportException( "Unsupported option <locale/> '" + this.locale + "'" );
+ }
+ }
+ }
+
+ /**
+ * Checks for the validity of the Standard Doclet options.
+ * <br/>
+ * For example, throw an exception if <nohelp/> and <helpfile/> options are used together.
+ *
+ * @throws MavenReportException if error or conflict found
+ */
+ private void validateStandardDocletOptions()
+ throws MavenReportException
+ {
+ // docencoding
+ if ( StringUtils.isNotEmpty( getDocencoding() ) && !JavadocUtil.validateEncoding( getDocencoding() ) )
+ {
+ throw new MavenReportException( "Unsupported option <docencoding/> '" + getDocencoding() + "'" );
+ }
+
+ // charset
+ if ( StringUtils.isNotEmpty( getCharset() ) && !JavadocUtil.validateEncoding( getCharset() ) )
+ {
+ throw new MavenReportException( "Unsupported option <charset/> '" + getCharset() + "'" );
+ }
+
+ // helpfile
+ if ( StringUtils.isNotEmpty( helpfile ) && nohelp )
+ {
+ throw new MavenReportException( "Option <nohelp/> conflicts with <helpfile/>" );
+ }
+
+ // overview
+ if ( ( getOverview() != null ) && nooverview )
+ {
+ throw new MavenReportException( "Option <nooverview/> conflicts with <overview/>" );
+ }
+
+ // index
+ if ( splitindex && noindex )
+ {
+ throw new MavenReportException( "Option <noindex/> conflicts with <splitindex/>" );
+ }
+
+ // stylesheet
+ if ( StringUtils.isNotEmpty( stylesheet ) && !( stylesheet.equalsIgnoreCase( "maven" )
+ || stylesheet.equalsIgnoreCase( "java" ) ) )
+ {
+ throw new MavenReportException( "Option <stylesheet/> supports only \"maven\" or \"java\" value." );
+ }
+ }
+
+ /**
+ * Add Standard Javadoc Options.
+ * <br/>
+ * The <a href="package-summary.html#Standard_Javadoc_Options">package documentation</a> details the
+ * Standard Javadoc Options wrapped by this Plugin.
+ *
+ * @param javadocOutputDirectory not null
+ * @param arguments not null
+ * @param allSourcePaths not null
+ * @throws MavenReportException if any
+ * @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>
+ */
+ private void addJavadocOptions( File javadocOutputDirectory,
+ List<String> arguments,
+ Map<String, Collection<Path>> allSourcePaths,
+ Set<OfflineLink> offlineLinks )
+ throws MavenReportException
+ {
+ Collection<Path> sourcePaths = collect( allSourcePaths.values() );
+
+ validateJavadocOptions();
+
+ // see com.sun.tools.javadoc.Start#parseAndExecute(String argv[])
+ addArgIfNotEmpty( arguments, "-locale", JavadocUtil.quotedArgument( this.locale ) );
+
+ // all options in alphabetical order
+
+ if ( old && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_4 ) )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Javadoc 1.4+ doesn't support the -1.1 switch anymore. Ignore this option." );
+ }
+ }
+ else
+ {
+ addArgIf( arguments, old, "-1.1" );
+ }
+
+ addArgIfNotEmpty( arguments, "-bootclasspath", JavadocUtil.quotedPathArgument( getBootclassPath() ) );
+
+ if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
+ {
+ addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_5 );
+ }
+
+ Map<String, MavenProject> reactorKeys = new HashMap<>( reactorProjects.size() );
+ for ( MavenProject reactorProject : reactorProjects )
+ {
+ reactorKeys.put( ArtifactUtils.versionlessKey( reactorProject.getGroupId(),
+ reactorProject.getArtifactId() ), reactorProject );
+ }
+
+ Map<String, JavaModuleDescriptor> allModuleDescriptors = new HashMap<>();
+
+ boolean supportModulePath = javadocRuntimeVersion.isAtLeast( "9" )
+ && ( source == null || JavaVersion.parse( source ).isAtLeast( "9" ) )
+ && ( release == null || JavaVersion.parse( release ).isAtLeast( "9" ) );
+
+ if ( supportModulePath )
+ {
+ for ( Map.Entry<String, Collection<Path>> entry : allSourcePaths.entrySet() )
+ {
+ MavenProject entryProject = reactorKeys.get( entry.getKey() );
+
+ File artifactFile;
+ if ( entryProject != null )
+ {
+ artifactFile = getArtifactFile( entryProject );
+ }
+ else
+ {
+ artifactFile = project.getArtifactMap().get( entry.getKey() ).getFile();
+ }
+ ResolvePathResult resolvePathResult = getResolvePathResult( artifactFile );
+
+ if ( resolvePathResult == null || resolvePathResult.getModuleNameSource() == ModuleNameSource.FILENAME )
+ {
+ File moduleDescriptor = findMainDescriptor( entry.getValue() );
+
+ if ( moduleDescriptor != null )
+ {
+ try
+ {
+ allModuleDescriptors.put( entry.getKey(),
+ locationManager.parseModuleDescriptor( moduleDescriptor ).getModuleDescriptor() );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ }
+ else
+ {
+ allModuleDescriptors.put( entry.getKey(), resolvePathResult.getModuleDescriptor() );
+ }
+ }
+ }
+
+ Collection<String> additionalModules = new ArrayList<>();
+
+ ResolvePathResult mainResolvePathResult = null;
+
+ Map<String, Collection<Path>> patchModules = new HashMap<>();
+
+ Path moduleSourceDir = null;
+ if ( supportModulePath && !allModuleDescriptors.isEmpty() )
+ {
+ Collection<String> unnamedProjects = new ArrayList<>();
+ for ( Map.Entry<String, Collection<Path>> projectSourcepaths : allSourcePaths.entrySet() )
+ {
+ MavenProject aggregatedProject = reactorKeys.get( projectSourcepaths.getKey() );
+ if ( aggregatedProject != null && !"pom".equals( aggregatedProject.getPackaging() ) )
+ {
+ ResolvePathResult result = null;
+
+ // Prefer jar over outputDirectory, since it may may contain an automatic module name
+ File artifactFile = getArtifactFile( aggregatedProject );
+ if ( artifactFile != null )
+ {
+ ResolvePathRequest<File> request = ResolvePathRequest.ofFile( artifactFile );
+ try
+ {
+ result = locationManager.resolvePath( request );
+ }
+ catch ( RuntimeException e )
+ {
+ // most likely an invalid module name based on filename
+ if ( !"java.lang.module.FindException".equals( e.getClass().getName() ) )
+ {
+ throw e;
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ else
+ {
+ File moduleDescriptor = findMainDescriptor( projectSourcepaths.getValue() );
+
+ if ( moduleDescriptor != null )
+ {
+ try
+ {
+ result = locationManager.parseModuleDescriptor( moduleDescriptor );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ }
+
+ if ( result != null && result.getModuleDescriptor() != null )
+ {
+ moduleSourceDir = javadocOutputDirectory.toPath().resolve( "src" );
+ try
+ {
+ moduleSourceDir = Files.createDirectories( moduleSourceDir );
+
+ additionalModules.add( result.getModuleDescriptor().name() );
+
+ patchModules.put( result.getModuleDescriptor().name(), projectSourcepaths.getValue() );
+
+ Path modulePath = moduleSourceDir.resolve( result.getModuleDescriptor().name() );
+ if ( !Files.isDirectory( modulePath ) )
+ {
+ Files.createDirectory( modulePath );
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ else
+ {
+ unnamedProjects.add( projectSourcepaths.getKey() );
+ }
+
+ if ( aggregatedProject.equals( getProject() ) )
+ {
+ mainResolvePathResult = result;
+ }
+ }
+ else
+ {
+ // todo
+ getLog().error( "no reactor project: " + projectSourcepaths.getKey() );
+ }
+ }
+
+ if ( !unnamedProjects.isEmpty() )
+ {
+ getLog().error( "Creating an aggregated report for both named and unnamed modules is not possible." );
+ getLog().error( "Ensure that every module has a module descriptor or is a jar with a MANIFEST.MF "
+ + "containing an Automatic-Module-Name." );
+ getLog().error( "Fix the following projects:" );
+ for ( String unnamedProject : unnamedProjects )
+ {
+ getLog().error( " - " + unnamedProject );
+ }
+ throw new MavenReportException( "Aggregator report contains named and unnamed modules" );
+ }
+
+ if ( mainResolvePathResult != null
+ && ModuleNameSource.MANIFEST.equals( mainResolvePathResult.getModuleNameSource() ) )
+ {
+ arguments.add( "--add-modules" );
+ arguments.add( "ALL-MODULE-PATH" );
+ }
+ }
+
+ // MJAVADOC-506
+ boolean moduleDescriptorSource = false;
+ for ( Path sourcepath : sourcePaths )
+ {
+ if ( Files.isRegularFile( sourcepath.resolve( "module-info.java" ) ) )
+ {
+ moduleDescriptorSource = true;
+ break;
+ }
+ }
+
+ final ModuleNameSource mainModuleNameSource;
+ if ( mainResolvePathResult != null )
+ {
+ mainModuleNameSource = mainResolvePathResult.getModuleNameSource();
+ }
+ else
+ {
+ mainModuleNameSource = null;
+ }
+
+ if ( supportModulePath
+ && !isTest()
+ && ( isAggregator()
+ || ModuleNameSource.MODULEDESCRIPTOR.equals( mainModuleNameSource )
+ || ModuleNameSource.MANIFEST.equals( mainModuleNameSource ) ) )
+ {
+ List<File> pathElements = new ArrayList<>( getPathElements() );
+ File artifactFile = getArtifactFile( project );
+ if ( artifactFile != null )
+ {
+ pathElements.add( 0, artifactFile );
+ }
+
+ ResolvePathsRequest<File> request =
+ ResolvePathsRequest.ofFiles( pathElements );
+
+ String mainModuleName = null;
+ if ( mainResolvePathResult != null )
+ {
+ request.setModuleDescriptor( mainResolvePathResult.getModuleDescriptor() );
+ mainModuleName = mainResolvePathResult.getModuleDescriptor().name();
+ }
+
+ request.setAdditionalModules( additionalModules );
+
+ try
+ {
+ ResolvePathsResult<File> result = locationManager.resolvePaths( request );
+
+ Set<File> modulePathElements = new HashSet<>( result.getModulepathElements().keySet() ) ;
+
+ Collection<File> classPathElements = new ArrayList<>( result.getClasspathElements().size() );
+
+ for ( File file : result.getClasspathElements() )
+ {
+ if ( file.isDirectory() && new File( file, "module-info.class" ).exists() )
+ {
+ modulePathElements.add( file );
+ }
+ else if ( ModuleNameSource.MANIFEST.equals( mainModuleNameSource ) )
+ {
+ ModuleNameSource depModuleNameSource =
+ locationManager.resolvePath( ResolvePathRequest.ofFile( file ) ).getModuleNameSource();
+ if ( ModuleNameSource.MODULEDESCRIPTOR.equals( depModuleNameSource )
+ || ModuleNameSource.MANIFEST.equals( depModuleNameSource ) )
+ {
+ modulePathElements.add( file );
+ }
+ else
+ {
+ patchModules.get( mainModuleName ).add( file.toPath() );
+ }
+ }
+ else
+ {
+ classPathElements.add( file );
+ }
+ }
+
+ /* MJAVADOC-620: also add all JARs where module-name-guessing leads to a FindException: */
+ for ( Entry<File, Exception> pathExceptionEntry : result.getPathExceptions().entrySet() )
+ {
+ Exception exception = pathExceptionEntry.getValue();
+ // For Java < 9 compatibility, reference FindException by name:
+ if ( "java.lang.module.FindException".equals( exception.getClass().getName() ) )
+ {
+ File jarPath = pathExceptionEntry.getKey();
+ classPathElements.add( jarPath );
+ }
+ }
+
+ String classpath = StringUtils.join( classPathElements.iterator(), File.pathSeparator );
+ addArgIfNotEmpty( arguments, "--class-path", JavadocUtil.quotedPathArgument( classpath ), false,
+ false );
+
+ String modulepath =
+ StringUtils.join( modulePathElements.iterator(), File.pathSeparator );
+ addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ), false,
+ false );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ else if ( supportModulePath && moduleDescriptorSource && !isTest() )
+ {
+ String modulepath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
+ addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ) , false, false );
+ }
+ else
+ {
+ String classpath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
+ addArgIfNotEmpty( arguments, "-classpath", JavadocUtil.quotedPathArgument( classpath ) , false, false );
+ }
+
+ for ( Entry<String, Collection<Path>> entry : patchModules.entrySet() )
+ {
+ addArgIfNotEmpty( arguments, "--patch-module", entry.getKey() + '='
+ + JavadocUtil.quotedPathArgument( getSourcePath( entry.getValue() ) ),
+ false, false );
+ }
+
+ if ( StringUtils.isNotEmpty( doclet ) )
+ {
+ addArgIfNotEmpty( arguments, "-doclet", JavadocUtil.quotedArgument( doclet ) );
+ addArgIfNotEmpty( arguments, "-docletpath", JavadocUtil.quotedPathArgument( getDocletPath() ) );
+ }
+
+ if ( StringUtils.isEmpty( encoding ) )
+ {
+ getLog().warn(
+ "Source files encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
+ + ", i.e. build is platform dependent!" );
+ }
+ addArgIfNotEmpty( arguments, "-encoding", JavadocUtil.quotedArgument( getEncoding() ) );
+
+ addArgIfNotEmpty( arguments, "-extdirs",
+ JavadocUtil.quotedPathArgument( JavadocUtil.unifyPathSeparator( extdirs ) ) );
+
+ if ( ( getOverview() != null ) && ( getOverview().exists() ) )
+ {
+ addArgIfNotEmpty( arguments, "-overview",
+ JavadocUtil.quotedPathArgument( getOverview().getAbsolutePath() ) );
+ }
+
+ arguments.add( getAccessLevel() );
+
+ if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
+ {
+ addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_5 );
+ }
+
+ if ( release != null )
+ {
+ arguments.add( "--release" );
+ arguments.add( release );
+ }
+ else
+ {
+ addArgIfNotEmpty( arguments, "-source", JavadocUtil.quotedArgument( source ), SINCE_JAVADOC_1_4 );
+ }
+
+ if ( ( StringUtils.isEmpty( sourcepath ) ) && ( StringUtils.isNotEmpty( subpackages ) ) )
+ {
+ sourcepath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
+ }
+
+ if ( moduleSourceDir == null )
+ {
+ addArgIfNotEmpty( arguments, "-sourcepath",
+ JavadocUtil.quotedPathArgument( getSourcePath( sourcePaths ) ), false, false );
+ }
+ else if ( mainResolvePathResult == null
+ || ModuleNameSource.MODULEDESCRIPTOR.equals( mainResolvePathResult.getModuleNameSource() ) )
+ {
+ addArgIfNotEmpty( arguments, "--module-source-path",
+ JavadocUtil.quotedPathArgument( moduleSourceDir.toString() ) );
+ }
+
+
+ if ( StringUtils.isNotEmpty( sourcepath ) && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
+ {
+ addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_5 );
+ }
+
+ // [MJAVADOC-497] must be after sourcepath is recalculated, since getExcludedPackages() depends on it
+ addArgIfNotEmpty( arguments, "-exclude", getExcludedPackages( sourcePaths ), SINCE_JAVADOC_1_4 );
+
+ addArgIf( arguments, verbose, "-verbose" );
+
+ if ( additionalOptions != null && additionalOptions.length > 0 )
+ {
+ for ( String additionalOption : additionalOptions )
+ {
+ arguments.add( additionalOption.replaceAll( "(?<!\\\\)\\\\(?!\\\\|:)", "\\\\" ) );
+ }
+ }
+ }
+
+ private ResolvePathResult getResolvePathResult( File artifactFile )
+ {
+ if ( artifactFile == null )
+ {
+ return null;
+ }
+
+ ResolvePathResult resolvePathResult = null;
+ ResolvePathRequest<File> resolvePathRequest = ResolvePathRequest.ofFile( artifactFile );
+ try
+ {
+ resolvePathResult = locationManager.resolvePath( resolvePathRequest );
+
+ // happens when artifactFile is a directory without module descriptor
+ if ( resolvePathResult.getModuleDescriptor() == null )
+ {
+ return null;
+ }
+ }
+ catch ( IOException | RuntimeException /* e.g java.lang.module.FindException */ e )
+ {
+ Throwable cause = e;
+ while ( cause.getCause() != null )
+ {
+ cause = cause.getCause();
+ }
+ getLog().warn( e.getMessage() );
+ }
+ return resolvePathResult;
+ }
+
+ private File findMainDescriptor( Collection<Path> roots ) throws MavenReportException
+ {
+ for ( Map.Entry<Path, Collection<String>> entry : getFiles( roots ).entrySet() )
+ {
+ if ( entry.getValue().contains( "module-info.java" ) )
+ {
+ return entry.getKey().resolve( "module-info.java" ).toFile();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Add Standard Doclet Options.
+ * <br/>
+ * The <a href="package-summary.html#Standard_Doclet_Options">package documentation</a> details the
+ * Standard Doclet Options wrapped by this Plugin.
+ *
+ * @param javadocOutputDirectory not null
+ * @param arguments not null
+ * @throws MavenReportException if any
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#standard">
+ * http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#standard</a>
+ */
+ private void addStandardDocletOptions( File javadocOutputDirectory,
+ List<String> arguments,
+ Set<OfflineLink> offlineLinks )
+ throws MavenReportException
+ {
+ validateStandardDocletOptions();
+
+ // all options in alphabetical order
+
+ addArgIf( arguments, author, "-author" );
+
+ addArgIfNotEmpty( arguments, "-bottom", JavadocUtil.quotedArgument( getBottomText() ), false, false );
+
+ if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
+ {
+ addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_4 );
+ }
+
+ addArgIfNotEmpty( arguments, "-charset", JavadocUtil.quotedArgument( getCharset() ) );
+
+ addArgIfNotEmpty( arguments, "-d", JavadocUtil.quotedPathArgument( javadocOutputDirectory.toString() ) );
+
+ addArgIfNotEmpty( arguments, "-docencoding", JavadocUtil.quotedArgument( getDocencoding() ) );
+
+ addArgIf( arguments, docfilessubdirs, "-docfilessubdirs", SINCE_JAVADOC_1_4 );
+
+ addArgIf( arguments, StringUtils.isNotEmpty( doclint ), "-Xdoclint:" + getDoclint(), SINCE_JAVADOC_1_8 );
+
+ addArgIfNotEmpty( arguments, "-doctitle", JavadocUtil.quotedArgument( getDoctitle() ), false, false );
+
+ if ( docfilessubdirs )
+ {
+ addArgIfNotEmpty( arguments, "-excludedocfilessubdir",
+ JavadocUtil.quotedPathArgument( excludedocfilessubdir ), SINCE_JAVADOC_1_4 );
+ }
+
+ addArgIfNotEmpty( arguments, "-footer", JavadocUtil.quotedArgument( footer ), false, false );
+
+ addGroups( arguments );
+
+ addArgIfNotEmpty( arguments, "-header", JavadocUtil.quotedArgument( header ), false, false );
+
+ addArgIfNotEmpty( arguments, "-helpfile",
+ JavadocUtil.quotedPathArgument( getHelpFile( javadocOutputDirectory ) ) );
+
+ addArgIf( arguments, keywords, "-keywords", SINCE_JAVADOC_1_4_2 );
+
+ addLinkArguments( arguments );
+
+ addLinkofflineArguments( arguments, offlineLinks );
+
+ addArgIf( arguments, linksource, "-linksource", SINCE_JAVADOC_1_4 );
+
+ if ( sourcetab > 0 )
+ {
+ if ( javadocRuntimeVersion == SINCE_JAVADOC_1_4_2 )
+ {
+ addArgIfNotEmpty( arguments, "-linksourcetab", String.valueOf( sourcetab ) );
+ }
+ addArgIfNotEmpty( arguments, "-sourcetab", String.valueOf( sourcetab ), SINCE_JAVADOC_1_5 );
+ }
+
+ addArgIf( arguments, nocomment, "-nocomment", SINCE_JAVADOC_1_4 );
+
+ addArgIf( arguments, nodeprecated, "-nodeprecated" );
+
+ addArgIf( arguments, nodeprecatedlist, "-nodeprecatedlist" );
+
+ addArgIf( arguments, nohelp, "-nohelp" );
+
+ addArgIf( arguments, noindex, "-noindex" );
+
+ addArgIf( arguments, nonavbar, "-nonavbar" );
+
+ addArgIf( arguments, nooverview, "-nooverview" );
+
+ addArgIfNotEmpty( arguments, "-noqualifier", JavadocUtil.quotedArgument( noqualifier ), SINCE_JAVADOC_1_4 );
+
+ addArgIf( arguments, nosince, "-nosince" );
+
+ addArgIf( arguments, notimestamp, "-notimestamp", SINCE_JAVADOC_1_5 );
+
+ addArgIf( arguments, notree, "-notree" );
+
+ addArgIfNotEmpty( arguments, "-packagesheader", JavadocUtil.quotedArgument( packagesheader ),
+ SINCE_JAVADOC_1_4_2 );
+
+ if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) ) // Sun bug: 4714350
+ {
+ addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_4 );
+ }
+
+ addArgIf( arguments, serialwarn, "-serialwarn" );
+
+ addArgIf( arguments, splitindex, "-splitindex" );
+
+ addArgIfNotEmpty( arguments, "-stylesheetfile",
+ JavadocUtil.quotedPathArgument( getStylesheetFile( javadocOutputDirectory ) ) );
+
+ if ( StringUtils.isNotEmpty( sourcepath ) && !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
+ {
+ addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_4 );
+ }
+
+ addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet ), SINCE_JAVADOC_1_4 );
+ addTaglets( arguments );
+ addTagletsFromTagletArtifacts( arguments );
+ addArgIfNotEmpty( arguments, "-tagletpath", JavadocUtil.quotedPathArgument( getTagletPath() ),
+ SINCE_JAVADOC_1_4 );
+
+ addTags( arguments );
+
+ addArgIfNotEmpty( arguments, "-top", JavadocUtil.quotedArgument( top ), false, false, SINCE_JAVADOC_1_6 );
+
+ addArgIf( arguments, use, "-use" );
+
+ addArgIf( arguments, version, "-version" );
+
+ addArgIfNotEmpty( arguments, "-windowtitle", JavadocUtil.quotedArgument( getWindowtitle() ), false, false );
+ }
+
+ /**
+ * Add <code>groups</code> parameter to arguments.
+ *
+ * @param arguments not null
+ * @throws MavenReportException
+ */
+ private void addGroups( List<String> arguments )
+ throws MavenReportException
+ {
+ Set<Group> groups = collectGroups();
+ if ( isEmpty( groups ) )
+ {
+ return;
+ }
+
+ for ( Group group : groups )
+ {
+ if ( group == null || StringUtils.isEmpty( group.getTitle() ) || StringUtils.isEmpty(
+ group.getPackages() ) )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "A group option is empty. Ignore this option." );
+ }
+ }
+ else
+ {
+ String groupTitle = StringUtils.replace( group.getTitle(), ",", "," );
+ addArgIfNotEmpty( arguments, "-group",
+ JavadocUtil.quotedArgument( groupTitle ) + " " + JavadocUtil.quotedArgument(
+ group.getPackages() ), true );
+ }
+ }
+ }
+
+ /**
+ * Add <code>tags</code> parameter to arguments.
+ *
+ * @param arguments not null
+ * @throws MavenReportException
+ */
+ private void addTags( List<String> arguments )
+ throws MavenReportException
+ {
+ Set<Tag> tags = collectTags();
+
+ if ( isEmpty( tags ) )
+ {
+ return;
+ }
+
+ for ( Tag tag : tags )
+ {
+ if ( StringUtils.isEmpty( tag.getName() ) )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "A tag name is empty. Ignore this option." );
+ }
+ }
+ else
+ {
+ String value = "\"" + tag.getName();
+ if ( StringUtils.isNotEmpty( tag.getPlacement() ) )
+ {
+ value += ":" + tag.getPlacement();
+ if ( StringUtils.isNotEmpty( tag.getHead() ) )
+ {
+ value += ":" + tag.getHead();
+ }
+ }
+ value += "\"";
+ addArgIfNotEmpty( arguments, "-tag", value, SINCE_JAVADOC_1_4 );
+ }
+ }
+ }
+
+ /**
+ * Add <code>taglets</code> parameter to arguments.
+ *
+ * @param arguments not null
+ */
+ private void addTaglets( List<String> arguments )
+ {
+ if ( taglets == null )
+ {
+ return;
+ }
+
+ for ( Taglet taglet1 : taglets )
+ {
+ if ( ( taglet1 == null ) || ( StringUtils.isEmpty( taglet1.getTagletClass() ) ) )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "A taglet option is empty. Ignore this option." );
+ }
+ }
+ else
+ {
+ addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet1.getTagletClass() ),
+ SINCE_JAVADOC_1_4 );
+ }
+ }
+ }
+
+ /**
+ * Auto-detect taglets class name from <code>tagletArtifacts</code> and add them to arguments.
+ *
+ * @param arguments not null
+ * @throws MavenReportException if any
+ * @see JavadocUtil#getTagletClassNames(File)
+ */
+ private void addTagletsFromTagletArtifacts( List<String> arguments )
+ throws MavenReportException
+ {
+ Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
+ if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
+ {
+ tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
+ }
+
+ if ( includeDependencySources )
+ {
+ try
+ {
+ resolveDependencyBundles();
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException(
+ "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
+ }
+
+ if ( isNotEmpty( dependencyJavadocBundles ) )
+ {
+ for ( JavadocBundle bundle : dependencyJavadocBundles )
+ {
+ JavadocOptions options = bundle.getOptions();
+ if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
+ {
+ tArtifacts.addAll( options.getTagletArtifacts() );
+ }
+ }
+ }
+ }
+
+ if ( isEmpty( tArtifacts ) )
+ {
+ return;
+ }
+
+ List<String> tagletsPath = new ArrayList<>();
+
+ for ( TagletArtifact aTagletArtifact : tArtifacts )
+ {
+ if ( ( StringUtils.isNotEmpty( aTagletArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
+ aTagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty( aTagletArtifact.getVersion() ) ) )
+ {
+ Artifact artifact;
+ try
+ {
+ artifact = createAndResolveArtifact( aTagletArtifact );
+ }
+ catch ( ArtifactResolverException e )
+ {
+ throw new MavenReportException( "Unable to resolve artifact:" + aTagletArtifact, e );
+ }
+
+ tagletsPath.add( artifact.getFile().getAbsolutePath() );
+ }
+ }
+
+ tagletsPath = JavadocUtil.pruneFiles( tagletsPath );
+
+ for ( String tagletJar : tagletsPath )
+ {
+ if ( !tagletJar.toLowerCase( Locale.ENGLISH ).endsWith( ".jar" ) )
+ {
+ continue;
+ }
+
+ List<String> tagletClasses;
+ try
+ {
+ tagletClasses = JavadocUtil.getTagletClassNames( new File( tagletJar ) );
+ }
+ catch ( IOException e )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
+ + "'. Try to specify them with <taglets/>." );
+ }
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().debug( "IOException: " + e.getMessage(), e );
+ }
+ continue;
+ }
+ catch ( ClassNotFoundException e )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
+ + "'. Try to specify them with <taglets/>." );
+ }
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().debug( "ClassNotFoundException: " + e.getMessage(), e );
+ }
+ continue;
+ }
+ catch ( NoClassDefFoundError e )
+ {
+ if ( getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
+ + "'. Try to specify them with <taglets/>." );
+ }
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().debug( "NoClassDefFoundError: " + e.getMessage(), e );
+ }
+ continue;
+ }
+
+ if ( tagletClasses != null && !tagletClasses.isEmpty() )
+ {
+ for ( String tagletClass : tagletClasses )
+ {
+ addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( tagletClass ),
+ SINCE_JAVADOC_1_4 );
+ }
+ }
+ }
+ }
+
+ /**
+ * Execute the Javadoc command line
+ *
+ * @param cmd not null
+ * @param javadocOutputDirectory not null
+ * @throws MavenReportException if any errors occur
+ */
+ private void executeJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
+ throws MavenReportException
+ {
+ if ( staleDataPath != null )
+ {
+ if ( !isUpToDate( cmd ) )
+ {
+ doExecuteJavadocCommandLine( cmd, javadocOutputDirectory );
+ StaleHelper.writeStaleData( cmd, staleDataPath.toPath() );
+ }
+ }
+ else
+ {
+ doExecuteJavadocCommandLine( cmd, javadocOutputDirectory );
+ }
+ }
+
+ /**
+ * Check if the javadoc is uptodate or not
+ *
+ * @param cmd not null
+ * @return <code>true</code> is the javadoc is uptodate, <code>false</code> otherwise
+ * @throws MavenReportException if any error occur
+ */
+ private boolean isUpToDate( Commandline cmd )
+ throws MavenReportException
+ {
+ try
+ {
+ String curdata = StaleHelper.getStaleData( cmd );
+ Path cacheData = staleDataPath.toPath();
+ String prvdata;
+ if ( Files.isRegularFile( cacheData ) )
+ {
+ prvdata = new String( Files.readAllBytes( cacheData ), StandardCharsets.UTF_8 );
+ }
+ else
+ {
+ prvdata = null;
+ }
+ if ( curdata.equals( prvdata ) )
+ {
+ getLog().info( "Skipping javadoc generation, everything is up to date." );
+ return true;
+ }
+ else
+ {
+ if ( prvdata == null )
+ {
+ getLog().info( "No previous run data found, generating javadoc." );
+ }
+ else
+ {
+ getLog().info( "Configuration changed, re-generating javadoc." );
+ }
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Error checking uptodate status", e );
+ }
+ return false;
+ }
+
+ /**
+ * Execute the Javadoc command line
+ *
+ * @param cmd not null
+ * @param javadocOutputDirectory not null
+ * @throws MavenReportException if any errors occur
+ */
+ private void doExecuteJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
+ throws MavenReportException
+ {
+ if ( getLog().isDebugEnabled() )
+ {
+ // no quoted arguments
+ getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) );
+ }
+
+ String cmdLine = null;
+ if ( debug )
+ {
+ cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
+
+ writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
+ }
+
+ CommandLineUtils.StringStreamConsumer err = new JavadocUtil.JavadocOutputStreamConsumer();
+ CommandLineUtils.StringStreamConsumer out = new JavadocUtil.JavadocOutputStreamConsumer();
+ try
+ {
+ int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
+
+ String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() );
+
+ if ( exitCode != 0 )
+ {
+ if ( cmdLine == null )
+ {
+ cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
+ }
+ writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
+
+ if ( StringUtils.isNotEmpty( output ) && StringUtils.isEmpty( err.getOutput() )
+ && isJavadocVMInitError( output ) )
+ {
+ throw new MavenReportException( output + '\n' + '\n' + JavadocUtil.ERROR_INIT_VM + '\n'
+ + "Or, try to reduce the Java heap size for the Javadoc goal using "
+ + "-Dminmemory=<size> and -Dmaxmemory=<size>." + '\n' + '\n' + "Command line was: " + cmdLine
+ + '\n' + '\n' + "Refer to the generated Javadoc files in '" + javadocOutputDirectory
+ + "' dir.\n" );
+ }
+
+ if ( StringUtils.isNotEmpty( output ) )
+ {
+ getLog().info( output );
+ }
+
+ StringBuilder msg = new StringBuilder( "\nExit code: " );
+ msg.append( exitCode );
+ if ( StringUtils.isNotEmpty( err.getOutput() ) )
+ {
+ msg.append( " - " ).append( err.getOutput() );
+ }
+ msg.append( '\n' );
+ msg.append( "Command line was: " ).append( cmdLine ).append( '\n' ).append( '\n' );
+
+ msg.append( "Refer to the generated Javadoc files in '" ).append( javadocOutputDirectory )
+ .append( "' dir.\n" );
+
+ throw new MavenReportException( msg.toString() );
+ }
+
+ if ( StringUtils.isNotEmpty( output ) )
+ {
+ getLog().info( output );
+ }
+ }
+ catch ( CommandLineException e )
+ {
+ throw new MavenReportException( "Unable to execute javadoc command: " + e.getMessage(), e );
+ }
+
+ // ----------------------------------------------------------------------
+ // Handle Javadoc warnings
+ // ----------------------------------------------------------------------
+
+ if ( StringUtils.isNotEmpty( err.getOutput() ) && getLog().isWarnEnabled() )
+ {
+ getLog().warn( "Javadoc Warnings" );
+
+ StringTokenizer token = new StringTokenizer( err.getOutput(), "\n" );
+ while ( token.hasMoreTokens() )
+ {
+ String current = token.nextToken().trim();
+
+ getLog().warn( current );
+ }
+ }
+
+ if ( StringUtils.isNotEmpty( err.getOutput() ) && failOnWarnings )
+ {
+ throw new MavenReportException( "Project contains Javadoc Warnings" );
+ }
+ }
+
+ /**
+ * Patches the given Javadoc output directory to work around CVE-2013-1571
+ * (see http://www.kb.cert.org/vuls/id/225657).
+ *
+ * @param javadocOutputDirectory directory to scan for vulnerabilities
+ * @param outputEncoding encoding used by the javadoc tool (-docencoding parameter).
+ * If {@code null}, the platform's default encoding is used (like javadoc does).
+ * @return the number of patched files
+ */
+ private int fixFrameInjectionBug( File javadocOutputDirectory, String outputEncoding )
+ throws IOException
+ {
+ final String fixData;
+ InputStream in = null;
+ try
+ {
+ in = this.getClass().getResourceAsStream( "frame-injection-fix.txt" );
+ if ( in == null )
+ {
+ throw new FileNotFoundException( "Missing resource 'frame-injection-fix.txt' in classpath." );
+ }
+ fixData = StringUtils.unifyLineSeparators( IOUtil.toString( in, "US-ASCII" ) ).trim();
+ in.close();
+ in = null;
+ }
+ finally
+ {
+ IOUtil.close( in );
+ }
+
+ final DirectoryScanner ds = new DirectoryScanner();
+ ds.setBasedir( javadocOutputDirectory );
+ ds.setCaseSensitive( false );
+ ds.setIncludes( new String[]{ "**/index.html", "**/index.htm", "**/toc.html", "**/toc.htm" } );
+ ds.addDefaultExcludes();
+ ds.scan();
+ int patched = 0;
+ for ( String f : ds.getIncludedFiles() )
+ {
+ final File file = new File( javadocOutputDirectory, f );
+ // we load the whole file as one String (toc/index files are
+ // generally small, because they only contain frameset declaration):
+ final String fileContents = FileUtils.fileRead( file, outputEncoding );
+ // check if file may be vulnerable because it was not patched with "validURL(url)":
+ if ( !StringUtils.contains( fileContents, "function validURL(url) {" ) )
+ {
+ // we need to patch the file!
+ final String patchedFileContents =
+ StringUtils.replaceOnce( fileContents, "function loadFrames() {", fixData );
+ if ( !patchedFileContents.equals( fileContents ) )
+ {
+ FileUtils.fileWrite( file, outputEncoding, patchedFileContents );
+ patched++;
+ }
+ }
+ }
+ return patched;
+ }
+
+ /**
+ * @param outputFile not nul
+ * @param inputResourceName a not null resource in <code>src/main/java</code>, <code>src/main/resources</code> or
+ * <code>src/main/javadoc</code> or in the Javadoc plugin dependencies.
+ * @return the resource file absolute path as String
+ * @since 2.6
+ */
+ private String getResource( File outputFile, String inputResourceName )
+ {
+ if ( inputResourceName.startsWith( "/" ) )
+ {
+ inputResourceName = inputResourceName.replaceFirst( "//*", "" );
+ }
+
+ List<String> classPath = new ArrayList<>();
+ classPath.add( project.getBuild().getSourceDirectory() );
+
+ URL resourceURL = getResource( classPath, inputResourceName );
+ if ( resourceURL != null )
+ {
+ getLog().debug( inputResourceName + " found in the main src directory of the project." );
+ return FileUtils.toFile( resourceURL ).getAbsolutePath();
+ }
+
+ classPath.clear();
+ List<Resource> resources = project.getBuild().getResources();
+ for ( Resource resource : resources )
+ {
+ classPath.add( resource.getDirectory() );
+ }
+ resourceURL = getResource( classPath, inputResourceName );
+ if ( resourceURL != null )
+ {
+ getLog().debug( inputResourceName + " found in the main resources directories of the project." );
+ return FileUtils.toFile( resourceURL ).getAbsolutePath();
+ }
+
+ if ( javadocDirectory.exists() )
+ {
+ classPath.clear();
+ classPath.add( javadocDirectory.getAbsolutePath() );
+ resourceURL = getResource( classPath, inputResourceName );
+ if ( resourceURL != null )
+ {
+ getLog().debug( inputResourceName + " found in the main javadoc directory of the project." );
+ return FileUtils.toFile( resourceURL ).getAbsolutePath();
+ }
+ }
+
+ classPath.clear();
+ final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
+ Plugin javadocPlugin = getPlugin( project, pluginId );
+ if ( javadocPlugin != null && javadocPlugin.getDependencies() != null )
+ {
+ List<Dependency> dependencies = javadocPlugin.getDependencies();
+ for ( Dependency dependency : dependencies )
+ {
+ JavadocPathArtifact javadocPathArtifact = new JavadocPathArtifact();
+ javadocPathArtifact.setGroupId( dependency.getGroupId() );
+ javadocPathArtifact.setArtifactId( dependency.getArtifactId() );
+ javadocPathArtifact.setVersion( dependency.getVersion() );
+ Artifact artifact = null;
+ try
+ {
+ artifact = createAndResolveArtifact( javadocPathArtifact );
+ }
+ catch ( Exception e )
+ {
+ logError( "Unable to retrieve the dependency: " + dependency + ". Ignored.", e );
+ }
+
+ if ( artifact != null && artifact.getFile().exists() )
+ {
+ classPath.add( artifact.getFile().getAbsolutePath() );
+ }
+ }
+ resourceURL = getResource( classPath, inputResourceName );
+ if ( resourceURL != null )
+ {
+ getLog().debug( inputResourceName + " found in javadoc plugin dependencies." );
+ try
+ {
+ JavadocUtil.copyResource( resourceURL, outputFile );
+
+ return outputFile.getAbsolutePath();
+ }
+ catch ( IOException e )
+ {
+ logError( "IOException: " + e.getMessage(), e );
+ }
+ }
+ }
+
+ getLog().warn( "Unable to find the resource '" + inputResourceName + "'. Using default Javadoc resources." );
+
+ return null;
+ }
+
+ /**
+ * @param classPath a not null String list of files where resource will be look up.
+ * @param resource a not null ressource to find in the class path.
+ * @return the resource from the given classpath or null if not found
+ * @see ClassLoader#getResource(String)
+ * @since 2.6
+ */
+ private URL getResource( final List<String> classPath, final String resource )
+ {
+ List<URL> urls = new ArrayList<>( classPath.size() );
+ for ( String filename : classPath )
+ {
+ try
+ {
+ urls.add( new File( filename ).toURL() );
+ }
+ catch ( MalformedURLException e )
+ {
+ getLog().error( "MalformedURLException: " + e.getMessage() );
+ }
+ }
+
+ ClassLoader javadocClassLoader = new URLClassLoader( urls.toArray( new URL[urls.size()] ), null );
+
+ return javadocClassLoader.getResource( resource );
+ }
+
+ /**
+ * Get the full javadoc goal. Loads the plugin's pom.properties to get the current plugin version.
+ *
+ * @return <code>org.apache.maven.plugins:maven-javadoc-plugin:CURRENT_VERSION:[test-]javadoc</code>
+ */
+ private String getFullJavadocGoal()
+ {
+ String javadocPluginVersion = null;
+ InputStream resourceAsStream = null;
+ try
+ {
+ String resource = "META-INF/maven/org.apache.maven.plugins/maven-javadoc-plugin/pom.properties";
+ resourceAsStream = AbstractJavadocMojo.class.getClassLoader().getResourceAsStream( resource );
+
+ if ( resourceAsStream != null )
+ {
+ Properties properties = new Properties();
+ properties.load( resourceAsStream );
+ resourceAsStream.close();
+ resourceAsStream = null;
+ if ( StringUtils.isNotEmpty( properties.getProperty( "version" ) ) )
+ {
+ javadocPluginVersion = properties.getProperty( "version" );
+ }
+ }
+ }
+ catch ( IOException e )
+ {
+ // nop
+ }
+ finally
+ {
+ IOUtil.close( resourceAsStream );
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "org.apache.maven.plugins:maven-javadoc-plugin:" );
+ if ( StringUtils.isNotEmpty( javadocPluginVersion ) )
+ {
+ sb.append( javadocPluginVersion ).append( ":" );
+ }
+
+ if ( this instanceof TestJavadocReport )
+ {
+ sb.append( "test-javadoc" );
+ }
+ else
+ {
+ sb.append( "javadoc" );
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Using Maven, a Javadoc link is given by <code>${project.url}/apidocs</code>.
+ *
+ * @return the detected Javadoc links using the Maven conventions for all modules defined in the current project
+ * or an empty list.
+ * @throws MavenReportException if any
+ * @see #detectOfflineLinks
+ * @see #reactorProjects
+ * @since 2.6
+ */
+ private List<OfflineLink> getModulesLinks()
+ throws MavenReportException
+ {
+ if ( !detectOfflineLinks || isAggregator() || reactorProjects == null )
+ {
+ return Collections.emptyList();
+ }
+
+ getLog().debug( "Trying to add links for modules..." );
+
+ Set<String> dependencyArtifactIds = new HashSet<>();
+ final Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
+ for ( Artifact artifact : dependencyArtifacts )
+ {
+ dependencyArtifactIds.add( artifact.getId() );
+ }
+
+ List<OfflineLink> modulesLinks = new ArrayList<>();
+ String javadocDirRelative = PathUtils.toRelative( project.getBasedir(), getOutputDirectory() );
+ for ( MavenProject p : reactorProjects )
+ {
+ if ( !dependencyArtifactIds.contains( p.getArtifact().getId() ) || ( p.getUrl() == null ) )
+ {
+ continue;
+ }
+
+ File location = new File( p.getBasedir(), javadocDirRelative );
+
+ if ( !location.exists() )
+ {
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().debug( "Javadoc directory not found: " + location );
+ }
+
+ String javadocGoal = getFullJavadocGoal();
+ getLog().info(
+ "The goal '" + javadocGoal + "' has not been previously called for the module: '" + p.getId()
+ + "'. Trying to invoke it..." );
+
+ File invokerDir = new File( project.getBuild().getDirectory(), "invoker" );
+ invokerDir.mkdirs();
+ File invokerLogFile = FileUtils.createTempFile( "maven-javadoc-plugin", ".txt", invokerDir );
+ try
+ {
+ JavadocUtil.invokeMaven( getLog(), new File( localRepository.getBasedir() ), p.getFile(),
+ Collections.singletonList( javadocGoal ), null, invokerLogFile );
+ }
+ catch ( MavenInvocationException e )
+ {
+ logError( "MavenInvocationException: " + e.getMessage(), e );
+
+ String invokerLogContent = JavadocUtil.readFile( invokerLogFile, null /* platform encoding */ );
+
+ // TODO: Why are we only interested in cases where the JVM won't start?
+ // [MJAVADOC-275][jdcasey] I changed the logic here to only throw an error WHEN
+ // the JVM won't start (opposite of what it was).
+ if ( invokerLogContent != null && invokerLogContent.contains( JavadocUtil.ERROR_INIT_VM ) )
+ {
+ throw new MavenReportException( e.getMessage(), e );
+ }
+ }
+ finally
+ {
+ // just create the directory to prevent repeated invocations..
+ if ( !location.exists() )
+ {
+ getLog().warn( "Creating fake javadoc directory to prevent repeated invocations: " + location );
+ location.mkdirs();
+ }
+ }
+ }
+
+ if ( location.exists() )
+ {
+ String url = getJavadocLink( p );
+
+ OfflineLink ol = new OfflineLink();
+ ol.setUrl( url );
+ ol.setLocation( location.getAbsolutePath() );
+
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().debug( "Added Javadoc offline link: " + url + " for the module: " + p.getId() );
+ }
+
+ modulesLinks.add( ol );
+ }
+ }
+
+ return modulesLinks;
+ }
+
+ /**
+ * Using Maven, a Javadoc link is given by <code>${project.url}/apidocs</code>.
+ *
+ * @return the detected Javadoc links using the Maven conventions for all dependencies defined in the current
+ * project or an empty list.
+ * @see #detectLinks
+ * @see #isValidJavadocLink
+ * @since 2.6
+ */
+ private List<String> getDependenciesLinks()
+ {
+ if ( !detectLinks )
+ {
+ return Collections.emptyList();
+ }
+
+ getLog().debug( "Trying to add links for dependencies..." );
+
+ List<String> dependenciesLinks = new ArrayList<>();
+
+ final Set<Artifact> dependencies = project.getDependencyArtifacts();
+ for ( Artifact artifact : dependencies )
+ {
+ if ( artifact.getFile() == null || !artifact.getFile().exists() )
+ {
+ continue;
+ }
+
+ try
+ {
+ MavenProject artifactProject =
+ mavenProjectBuilder.build( artifact, getProjectBuildingRequest( project ) ).getProject();
+
+ if ( StringUtils.isNotEmpty( artifactProject.getUrl() ) )
+ {
+ String url = getJavadocLink( artifactProject );
+
+ if ( isValidJavadocLink( url, true ) )
+ {
+ getLog().debug( "Added Javadoc link: " + url + " for " + artifactProject.getId() );
+
+ dependenciesLinks.add( url );
+ }
+ }
+ }
+ catch ( ProjectBuildingException e )
+ {
+ logError( "ProjectBuildingException for " + artifact.toString() + ": " + e.getMessage(), e );
+ }
+ }
+
+ return dependenciesLinks;
+ }
+
+ /**
+ * @return if {@link #detectJavaApiLink}, the Java API link based on the {@link #javaApiLinks} properties and the
+ * value of the <code>source</code> parameter in the
+ * <code>org.apache.maven.plugins:maven-compiler-plugin</code>
+ * defined in <code>${project.build.plugins}</code> or in <code>${project.build.pluginManagement}</code>,
+ * or the {@link #javadocRuntimeVersion}, or <code>null</code> if not defined.
+ * @see #detectJavaApiLink
+ * @see #javaApiLinks
+ * @see <a href="http://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source">source parameter</a>
+ * @since 2.6
+ */
+ protected final OfflineLink getDefaultJavadocApiLink()
+ {
+ if ( !detectJavaApiLink )
+ {
+ return null;
+ }
+
+ final JavaVersion javaApiversion;
+ if ( release != null )
+ {
+ javaApiversion = JavaVersion.parse( release );
+ }
+ else
+ {
+ final String pluginId = "org.apache.maven.plugins:maven-compiler-plugin";
+ String sourceConfigured = getPluginParameter( project, pluginId, "source" );
+ if ( sourceConfigured != null )
+ {
+ javaApiversion = JavaVersion.parse( sourceConfigured );
+ }
+ else
+ {
+ getLog().debug( "No maven-compiler-plugin defined in ${build.plugins} or in "
+ + "${project.build.pluginManagement} for the " + project.getId()
+ + ". Added Javadoc API link according the javadoc executable version i.e.: "
+ + javadocRuntimeVersion );
+
+ javaApiversion = javadocRuntimeVersion;
+ }
+ }
+
+ final String javaApiKey;
+ if ( javaApiversion.asMajor().isAtLeast( "9" ) )
+ {
+ javaApiKey = "api_" + javaApiversion.asMajor();
+ }
+ else
+ {
+ javaApiKey = "api_1." + javaApiversion.asMajor().toString().charAt( 0 );
+ }
+
+ final String javaApiLink;
+ if ( javaApiLinks != null && javaApiLinks.containsKey( javaApiKey ) )
+ {
+ javaApiLink = javaApiLinks.getProperty( javaApiKey );
+ }
+ else if ( javaApiversion.isAtLeast( "11" ) )
+ {
+ javaApiLink =
+ String.format( "https://docs.oracle.com/en/java/javase/%s/docs/api/", javaApiversion.getValue( 1 ) );
+ }
+ else if ( javaApiversion.asMajor().isAtLeast( "6" ) )
+ {
+ javaApiLink =
+ String.format( "https://docs.oracle.com/javase/%s/docs/api/", javaApiversion.asMajor().getValue( 1 ) );
+ }
+ else if ( javaApiversion.isAtLeast( "1.5" ) )
+ {
+ javaApiLink = "https://docs.oracle.com/javase/1.5.0/docs/api/";
+ }
+ else
+ {
+ javaApiLink = null;
+ }
+
+ if ( getLog().isDebugEnabled() )
+ {
+ if ( javaApiLink != null )
+ {
+ getLog().debug( "Found Java API link: " + javaApiLink );
+ }
+ else
+ {
+ getLog().debug( "No Java API link found." );
+ }
+ }
+
+ if ( javaApiLink == null )
+ {
+ return null;
+ }
+
+ final Path javaApiListFile;
+ final String resourceName;
+ if ( javaApiversion.isAtLeast( "10" ) )
+ {
+ javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "element-list" );
+ resourceName = "java-api-element-list-" + javaApiversion.toString().substring( 0, 2 );
+ }
+ else if ( javaApiversion.asMajor().isAtLeast( "9" ) )
+ {
+ javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
+ resourceName = "java-api-package-list-9";
+ }
+ else
+ {
+ javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
+ resourceName = "java-api-package-list-1." + javaApiversion.asMajor().toString().charAt( 0 );
+ }
+
+ OfflineLink link = new OfflineLink();
+ link.setLocation( javaApiListFile.getParent().toAbsolutePath().toString() );
+ link.setUrl( javaApiLink );
+
+ InputStream in = this.getClass().getResourceAsStream( resourceName );
+ if ( in != null )
+ {
+ try ( InputStream closableIS = in )
+ {
+ // TODO only copy when changed
+ Files.copy( closableIS, javaApiListFile, StandardCopyOption.REPLACE_EXISTING );
+ }
+ catch ( IOException ioe )
+ {
+ logError( "Can't get " + resourceName + ": " + ioe.getMessage(), ioe );
+ return null;
+ }
+ }
+
+ return link;
+ }
+
+ /**
+ * Follows all of the given links, and returns their last redirect locations. Ordering is kept.
+ * This is necessary because javadoc tool doesn't follow links, see JDK-8190312 (MJAVADOC-427, MJAVADOC-487)
+ *
+ * @param links Links to follow.
+ * @return Last redirect location of all the links.
+ */
+ private Set<String> followLinks( Set<String> links )
+ {
+ Set<String> redirectLinks = new LinkedHashSet<>( links.size() );
+ for ( String link : links )
+ {
+ try
+ {
+ redirectLinks.add( JavadocUtil.getRedirectUrl( new URI( link ).toURL(), settings ).toString() );
+ }
+ catch ( Exception e )
+ {
+ // only print in debug, it should have been logged already in warn/error because link isn't valid
+ getLog().debug( "Could not follow " + link + ". Reason: " + e.getMessage() );
+
+ // Even when link produces error it should be kept in the set because the error might be caused by
+ // incomplete redirect configuration on the server side.
+ // This partially restores the previous behaviour before fix for MJAVADOC-427
+ redirectLinks.add( link );
+ }
+ }
+ return redirectLinks;
+ }
+
+ /**
+ * @param link not null
+ * @param detecting <code>true</code> if the link is generated by
+ * <code>detectLinks</code>, or <code>false</code> otherwise
+ * @return <code>true</code> if the link has a <code>/package-list</code>, <code>false</code> otherwise.
+ * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javadoc.html#package-list">
+ * package-list spec</a>
+ * @since 2.6
+ */
+ protected boolean isValidJavadocLink( String link, boolean detecting )
+ {
+ try
+ {
+ final URI packageListUri;
+ final URI elementListUri;
+
+ if ( link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "http:" ) || link.trim().toLowerCase(
+ Locale.ENGLISH ).startsWith( "https:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith(
+ "ftp:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "file:" ) )
+ {
+ packageListUri = new URI( link + '/' + PACKAGE_LIST );
+ elementListUri = new URI( link + '/' + ELEMENT_LIST );
+ }
+ else
+ {
+ // links can be relative paths or files
+ File dir = new File( link );
+ if ( !dir.isAbsolute() )
+ {
+ dir = new File( getOutputDirectory(), link );
+ }
+ if ( !dir.isDirectory() )
+ {
+ if ( detecting )
+ {
+ getLog().warn( "The given File link: " + dir + " is not a dir." );
+ }
+ else
+ {
+ getLog().error( "The given File link: " + dir + " is not a dir." );
+ }
+ }
+ packageListUri = new File( dir, PACKAGE_LIST ).toURI();
+ elementListUri = new File( dir, ELEMENT_LIST ).toURI();
+ }
+
+
+ IOException elementListIOException = null;
+ try
+ {
+ if ( JavadocUtil.isValidElementList( elementListUri.toURL(), settings, validateLinks ) )
+ {
+ return true;
+ }
+ }
+ catch ( IOException e )
+ {
+ elementListIOException = e;
+ }
+
+ if ( JavadocUtil.isValidPackageList( packageListUri.toURL(), settings, validateLinks ) )
+ {
+ return true;
+ }
+
+ if ( getLog().isErrorEnabled() )
+ {
+ if ( detecting )
+ {
+ getLog().warn( "Invalid links: "
+ + link + " with /" + PACKAGE_LIST + " or / " + ELEMENT_LIST + ". Ignored it." );
+ }
+ else
+ {
+ getLog().error( "Invalid links: "
+ + link + " with /" + PACKAGE_LIST + " or / " + ELEMENT_LIST + ". Ignored it." );
+ }
+ }
+
+ return false;
+ }
+ catch ( URISyntaxException e )
+ {
+ if ( getLog().isErrorEnabled() )
+ {
+ if ( detecting )
+ {
+ getLog().warn( "Malformed link: " + e.getInput() + ". Ignored it." );
+ }
+ else
+ {
+ getLog().error( "Malformed link: " + e.getInput() + ". Ignored it." );
+ }
+ }
+ return false;
+ }
+ catch ( IOException e )
+ {
+ if ( getLog().isErrorEnabled() )
+ {
+ if ( detecting )
+ {
+ getLog().warn( "Error fetching link: " + link + ". Ignored it." );
+ }
+ else
+ {
+ getLog().error( "Error fetching link: " + link + ". Ignored it." );
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Write a debug javadoc script in case of command line error or in debug mode.
+ *
+ * @param cmdLine the current command line as string, not null.
+ * @param javadocOutputDirectory the output dir, not null.
+ * @see #executeJavadocCommandLine(Commandline, File)
+ * @since 2.6
+ */
+ private void writeDebugJavadocScript( String cmdLine, File javadocOutputDirectory )
+ {
+ File commandLineFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
+ commandLineFile.getParentFile().mkdirs();
+
+ try
+ {
+ FileUtils.fileWrite( commandLineFile.getAbsolutePath(), null /* platform encoding */, cmdLine );
+
+ if ( !SystemUtils.IS_OS_WINDOWS )
+ {
+ Runtime.getRuntime().exec( new String[]{ "chmod", "a+x", commandLineFile.getAbsolutePath() } );
+ }
+ }
+ catch ( IOException e )
+ {
+ logError( "Unable to write '" + commandLineFile.getName() + "' debug script file", e );
+ }
+ }
+
+ /**
+ * Check if the Javadoc JVM is correctly started or not.
+ *
+ * @param output the command line output, not null.
+ * @return <code>true</code> if Javadoc output command line contains Javadoc word, <code>false</code> otherwise.
+ * @see #executeJavadocCommandLine(Commandline, File)
+ * @since 2.6.1
+ */
+ private boolean isJavadocVMInitError( String output )
+ {
+ /*
+ * see main.usage and main.Building_tree keys from
+ * com.sun.tools.javadoc.resources.javadoc bundle in tools.jar
+ */
+ return !( output.contains( "Javadoc" ) || output.contains( "javadoc" ) );
+ }
+
+ // ----------------------------------------------------------------------
+ // Static methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * @param p not null
+ * @return the javadoc link based on the project url i.e. <code>${project.url}/${destDir}</code> where
+ * <code>destDir</code> is configued in the Javadoc plugin configuration (<code>apidocs</code> by default).
+ * @since 2.6
+ */
+ private static String getJavadocLink( MavenProject p )
+ {
+ if ( p.getUrl() == null )
+ {
+ return null;
+ }
+
+ String url = cleanUrl( p.getUrl() );
+ String destDir = "apidocs"; // see JavadocReport#destDir
+
+ final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
+ String destDirConfigured = getPluginParameter( p, pluginId, "destDir" );
+ if ( destDirConfigured != null )
+ {
+ destDir = destDirConfigured;
+ }
+
+ return url + "/" + destDir;
+ }
+
+ /**
+ * @param url could be null.
+ * @return the url cleaned or empty if url was null.
+ * @since 2.6
+ */
+ private static String cleanUrl( String url )
+ {
+ if ( url == null )
+ {
+ return "";
+ }
+
+ url = url.trim();
+ while ( url.endsWith( "/" ) )
+ {
+ url = url.substring( 0, url.lastIndexOf( "/" ) );
+ }
+
+ return url;
+ }
+
+ /**
+ * @param p not null
+ * @param pluginId not null key of the plugin defined in {@link org.apache.maven.model.Build#getPluginsAsMap()}
+ * or in {@link org.apache.maven.model.PluginManagement#getPluginsAsMap()}
+ * @return the Maven plugin defined in <code>${project.build.plugins}</code> or in
+ * <code>${project.build.pluginManagement}</code>, or <code>null</code> if not defined.
+ * @since 2.6
+ */
+ private static Plugin getPlugin( MavenProject p, String pluginId )
+ {
+ if ( ( p.getBuild() == null ) || ( p.getBuild().getPluginsAsMap() == null ) )
+ {
+ return null;
+ }
+
+ Plugin plugin = p.getBuild().getPluginsAsMap().get( pluginId );
+
+ if ( ( plugin == null ) && ( p.getBuild().getPluginManagement() != null ) && (
+ p.getBuild().getPluginManagement().getPluginsAsMap() != null ) )
+ {
+ plugin = p.getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
+ }
+
+ return plugin;
+ }
+
+ /**
+ * @param p not null
+ * @param pluginId not null
+ * @param param not null
+ * @return the simple parameter as String defined in the plugin configuration by <code>param</code> key
+ * or <code>null</code> if not found.
+ * @since 2.6
+ */
+ private static String getPluginParameter( MavenProject p, String pluginId, String param )
+ {
+// p.getGoalConfiguration( pluginGroupId, pluginArtifactId, executionId, goalId );
+ Plugin plugin = getPlugin( p, pluginId );
+ if ( plugin != null )
+ {
+ Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
+ if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
+ && StringUtils.isNotEmpty( xpp3Dom.getChild( param ).getValue() ) )
+ {
+ return xpp3Dom.getChild( param ).getValue();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Construct the output file for the generated javadoc-options XML file, after creating the
+ * javadocOptionsDir if necessary. This method does NOT write to the file in question.
+ *
+ * @return The options {@link File} file.
+ * @since 2.7
+ */
+ protected final File getJavadocOptionsFile()
+ {
+ if ( javadocOptionsDir != null && !javadocOptionsDir.exists() )
+ {
+ javadocOptionsDir.mkdirs();
+ }
+
+ return new File( javadocOptionsDir, "javadoc-options-" + getAttachmentClassifier() + ".xml" );
+ }
+
+ /**
+ * Generate a javadoc-options XML file, for either bundling with a javadoc-resources artifact OR
+ * supplying to a distro module in a includeDependencySources configuration, so the javadoc options
+ * from this execution can be reconstructed and merged in the distro build.
+ *
+ * @return {@link JavadocOptions}
+ * @throws IOException {@link IOException}
+ * @since 2.7
+ */
+ protected final JavadocOptions buildJavadocOptions()
+ throws IOException
+ {
+ JavadocOptions options = new JavadocOptions();
+
+ options.setBootclasspathArtifacts( toList( bootclasspathArtifacts ) );
+ options.setDocfilesSubdirsUsed( docfilessubdirs );
+ options.setDocletArtifacts( toList( docletArtifact, docletArtifacts ) );
+ options.setExcludedDocfilesSubdirs( excludedocfilessubdir );
+ options.setExcludePackageNames( toList( excludePackageNames ) );
+ options.setGroups( toList( groups ) );
+ options.setLinks( links );
+ options.setOfflineLinks( toList( offlineLinks ) );
+ options.setResourcesArtifacts( toList( resourcesArtifacts ) );
+ options.setTagletArtifacts( toList( tagletArtifact, tagletArtifacts ) );
+ options.setTaglets( toList( taglets ) );
+ options.setTags( toList( tags ) );
+
+ if ( getProject() != null && getJavadocDirectory() != null )
+ {
+ options.setJavadocResourcesDirectory(
+ toRelative( getProject().getBasedir(), getJavadocDirectory().getAbsolutePath() ) );
+ }
+
+ File optionsFile = getJavadocOptionsFile();
+
+ try ( Writer writer = WriterFactory.newXmlWriter( optionsFile ) )
+ {
+ new JavadocOptionsXpp3Writer().write( writer, options );
+ }
+
+ return options;
+ }
+
+ /**
+ * Override this if you need to provide a bundle attachment classifier, as in the case of test
+ * javadocs.
+ * @return The attachment classifier.
+ */
+ protected String getAttachmentClassifier()
+ {
+ return JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER;
+ }
+
+ /**
+ * Logs an error with throwable content only if in debug.
+ *
+ * @param message The message which should be announced.
+ * @param t The throwable part of the message.
+ */
+ protected void logError( String message, Throwable t )
+ {
+ if ( getLog().isDebugEnabled() )
+ {
+ getLog().error( message, t );
+ }
+ else
+ {
+ getLog().error( message );
+ }
+ }
+
+ /**
+ * @param prefix The prefix of the exception.
+ * @param e The exception.
+ * @throws MojoExecutionException {@link MojoExecutionException}
+ */
+ protected void failOnError( String prefix, Exception e )
+ throws MojoExecutionException
+ {
+ if ( failOnError )
+ {
+ if ( e instanceof RuntimeException )
+ {
+ throw (RuntimeException) e;
+ }
+ throw new MojoExecutionException( prefix + ": " + e.getMessage(), e );
+ }
+
+ getLog().error( prefix + ": " + e.getMessage(), e );
+ }
+}
diff --git a/tooling/maven/camel-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/StaleHelper.java b/tooling/maven/camel-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/StaleHelper.java
new file mode 100644
index 0000000..72b2e48
--- /dev/null
+++ b/tooling/maven/camel-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/StaleHelper.java
@@ -0,0 +1,181 @@
+package org.apache.maven.plugins.javadoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.maven.reporting.MavenReportException;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.cli.Commandline;
+
+/**
+ * Helper class to compute and write data used to detect a
+ * stale javadoc.
+ */
+public class StaleHelper
+{
+
+ /**
+ * Compute the data used to detect a stale javadoc
+ *
+ * @param cmd the command line
+ * @return the stale data
+ * @throws MavenReportException if an error occurs
+ */
+ public static String getStaleData( Commandline cmd )
+ throws MavenReportException
+ {
+ try
+ {
+ List<String> ignored = new ArrayList<>();
+ List<String> options = new ArrayList<>();
+ Path dir = cmd.getWorkingDirectory().toPath().toAbsolutePath().normalize();
+ String[] args = cmd.getCommandline();
+ Collections.addAll( options, args );
+ for ( String arg : args )
+ {
+ if ( arg.startsWith( "@" ) )
+ {
+ String name = arg.substring( 1 );
+ options.addAll( Files.readAllLines( dir.resolve( name ), StandardCharsets.UTF_8 ) );
+ ignored.add( name );
+ }
+ }
+ List<String> state = new ArrayList<>( options );
+ boolean cp = false;
+ boolean sp = false;
+ for ( String arg : options )
+ {
+ if ( cp )
+ {
+ String s = unquote( arg );
+ for ( String ps : s.split( File.pathSeparator ) )
+ {
+ Path p = dir.resolve( ps );
+ state.add( p + " = " + lastmod( p ) );
+ }
+ }
+ else if ( sp )
+ {
+ String s = unquote( arg );
+ for ( String ps : s.split( File.pathSeparator ) )
+ {
+ Path p = dir.resolve( ps );
+ for ( Path c : walk( p ) )
+ {
+ if ( Files.isRegularFile( c ) )
+ {
+ state.add( c + " = " + lastmod( c ) );
+ }
+ }
+ state.add( p + " = " + lastmod( p ) );
+ }
+ }
+ cp = "-classpath".equals( arg );
+ sp = "-sourcepath".equals( arg );
+ }
+ for ( Path p : walk( dir ) )
+ {
+ if ( Files.isRegularFile( p ) && !ignored.contains( p.getFileName().toString() ) )
+ {
+ state.add( p + " = " + lastmod( p ) );
+ }
+ }
+ return StringUtils.join( state.iterator(), SystemUtils.LINE_SEPARATOR );
+ }
+ catch ( Exception e )
+ {
+ throw new MavenReportException( "Unable to compute stale date", e );
+ }
+ }
+
+ /**
+ * Write the data used to detect a stale javadoc
+ *
+ * @param cmd the command line
+ * @param path the stale data path
+ * @throws MavenReportException if an error occurs
+ */
+ public static void writeStaleData( Commandline cmd, Path path )
+ throws MavenReportException
+ {
+ try
+ {
+ String curdata = getStaleData( cmd );
+ Files.createDirectories( path.getParent() );
+ FileUtils.fileWrite( path.toFile(), null /* platform encoding */, curdata );
+ }
+ catch ( IOException e )
+ {
+ throw new MavenReportException( "Error checking stale data", e );
+ }
+ }
+
+ private static Collection<Path> walk( Path dir )
+ {
+ try
+ {
+ Collection<Path> paths = new ArrayList<>();
+ for ( Path p : Files.newDirectoryStream( dir ) )
+ {
+ paths.add( p );
+ }
+ return paths;
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+ private static String unquote( String s )
+ {
+ if ( s.startsWith( "'" ) && s.endsWith( "'" ) )
+ {
+ return s.substring( 1, s.length() - 1 ).replaceAll( "\\\\'", "'" );
+ }
+ else
+ {
+ return s;
+ }
+ }
+
+ private static long lastmod( Path p )
+ {
+ try
+ {
+ return Files.getLastModifiedTime( p ).toMillis();
+ }
+ catch ( IOException e )
+ {
+ return 0;
+ }
+ }
+
+}