blob: e7c8bbcbc632e6700d190d9f85fdb1285df54a36 [file] [log] [blame]
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.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.Paths;
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.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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>
* @version $Id: AbstractJavadocMojo.java 1801354 2017-07-09 08:49:46Z rfscholte $
* @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 default Javadoc API urls according the
* <a href="http://www.oracle.com/technetwork/java/javase/documentation/api-jsp-136079.html">Sun API
* Specifications</a>:
* <pre>
* &lt;javaApiLinks&gt;
* &lt;property&gt;
* &lt;name&gt;api_1.3&lt;/name&gt;
* &lt;value&gt;http://docs.oracle.com/javase/1.3/docs/api/&lt;/value&gt;
* &lt;/property&gt;
* &lt;property&gt;
* &lt;name&gt;api_1.4&lt;/name&gt;
* &lt;value&gt;http://docs.oracle.com/javase/1.4.2/docs/api/&lt;/value&gt;
* &lt;/property&gt;
* &lt;property&gt;
* &lt;name&gt;api_1.5&lt;/name&gt;
* &lt;value&gt;http://docs.oracle.com/javase/1.5.0/docs/api/&lt;/value&gt;
* &lt;/property&gt;
* &lt;property&gt;
* &lt;name&gt;api_1.6&lt;/name&gt;
* &lt;value&gt;http://docs.oracle.com/javase/6/docs/api/&lt;/value&gt;
* &lt;/property&gt;
* &lt;property&gt;
* &lt;name&gt;api_1.7&lt;/name&gt;
* &lt;value&gt;http://docs.oracle.com/javase/7/docs/api/&lt;/value&gt;
* &lt;/property&gt;
* &lt;property&gt;
* &lt;name&gt;api_1.8&lt;/name&gt;
* &lt;value&gt;http://docs.oracle.com/javase/8/docs/api/&lt;/value&gt;
* &lt;/property&gt;
* &lt;/javaApiLinks&gt;
* </pre>
*
* @since 2.6
*/
public static final Properties DEFAULT_JAVA_API_LINKS = new Properties();
/**
* 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) &#x40;options &#x40;packages | &#x40;argfile | &#x40;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) &#x40;options &#x40;packages | &#x40;argfile | &#x40;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) &#x40;options &#x40;packages | &#x40;argfile | &#x40;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) &#x40;options &#x40;packages | &#x40;argfile | &#x40;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;
// ----------------------------------------------------------------------
// 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. This value should include quotes as necessary for
* parameters that include spaces. 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>
* &lt;additionalJOption&gt;-J-Xss128m&lt;/additionalJOption&gt;
* </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>
* &lt;additionalJOptions&gt;
* &lt;additionalJOption&gt;-J-Xmx1g &lt;/additionalJOption&gt;
* &lt;/additionalJOptions&gt;
* </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>
* &lt;resourcesArtifacts&gt;
* &lt;resourcesArtifact&gt;
* &lt;groupId&gt;external.group.id&lt;/groupId&gt;
* &lt;artifactId&gt;external-resources&lt;/artifactId&gt;
* &lt;version&gt;1.0&lt;/version&gt;
* &lt;/resourcesArtifact&gt;
* &lt;/resourcesArtifacts&gt;
* </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>
* &lt;docletArtifacts&gt;
* &lt;docletArtifact&gt;
* &lt;groupId&gt;com.sun.tools.doclets&lt;/groupId&gt;
* &lt;artifactId&gt;doccheck&lt;/artifactId&gt;
* &lt;version&gt;1.2b2&lt;/version&gt;
* &lt;/docletArtifact&gt;
* &lt;/docletArtifacts&gt;
* &lt;useStandardDocletOptions&gt;true&lt;/useStandardDocletOptions&gt;
* </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>
* &lt;dependency&gt;
* &lt;groupId&gt;commons-lang&lt;/groupId&gt;
* &lt;artifactId&gt;commons-lang&lt;/artifactId&gt;
* &lt;/dependency&gt;
* </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 the <a href="http://java.sun.com/reference/api/index.html">Sun Javadoc API</a>
* urls have been changed or to use custom urls for Javadoc API url.
* <br/>
* See
* <a href="./apidocs/org/apache/maven/plugin/javadoc/AbstractJavadocMojo.html#DEFAULT_JAVA_API_LINKS">Javadoc</a>
* for the default values.
* <br/>
*
* @see #DEFAULT_JAVA_API_LINKS
* @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>
* &lt;bootclasspathArtifacts&gt;
* &lt;bootclasspathArtifact&gt;
* &lt;groupId&gt;my-groupId&lt;/groupId&gt;
* &lt;artifactId&gt;my-artifactId&lt;/artifactId&gt;
* &lt;version&gt;my-version&lt;/version&gt;
* &lt;/bootclasspathArtifact&gt;
* &lt;/bootclasspathArtifacts&gt;
* </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>
* &lt;docletArtifact&gt;
* &lt;groupId&gt;com.sun.tools.doclets&lt;/groupId&gt;
* &lt;artifactId&gt;doccheck&lt;/artifactId&gt;
* &lt;version&gt;1.2b2&lt;/version&gt;
* &lt;/docletArtifact&gt;
* </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>
* &lt;docletArtifacts&gt;
* &lt;docletArtifact&gt;
* &lt;groupId&gt;com.sun.tools.doclets&lt;/groupId&gt;
* &lt;artifactId&gt;doccheck&lt;/artifactId&gt;
* &lt;version&gt;1.2b2&lt;/version&gt;
* &lt;/docletArtifact&gt;
* &lt;/docletArtifacts&gt;
* </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>
* &lt;excludePackageNames&gt;*.internal:org.acme.exclude1.*:org.acme.exclude2&lt;/excludePackageNames&gt;
* </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 &lt;nooverview/&gt;.
* <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;
/**
* 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>&lt;![CDATA[Copyright 2005, &lt;a href="http://www.mycompany.com">MyCompany, Inc.&lt;a>]]&gt;</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 &#169; {inceptionYear}&#x2013;{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>&#42;&#42;/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>
* &lt;groups&gt;
* &lt;group&gt;
* &lt;title&gt;Core Packages&lt;/title&gt;
* &lt;!-- To includes java.lang, java.lang.ref,
* java.lang.reflect and only java.util
* (i.e. not java.util.jar) --&gt;
* &lt;packages&gt;java.lang*:java.util&lt;/packages&gt;
* &lt;/group&gt;
* &lt;group&gt;
* &lt;title&gt;Extension Packages&lt;/title&gt;
* &nbsp;&lt;!-- To include javax.accessibility,
* javax.crypto, ... (among others) --&gt;
* &lt;packages&gt;javax.*&lt;/packages&gt;
* &lt;/group&gt;
* &lt;/groups&gt;
* </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( property = "groups" )
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 &lt;nohelp/&gt;.
* <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>
* &lt;helpfile&gt;path/to/your/resource/yourhelp-doc.html&lt;/helpfile&gt;
* </pre>
* Where <code>path/to/your/resource/yourhelp-doc.html</code> could be in <code>src/main/javadoc</code>.
* <pre>
* &lt;build&gt;
* &lt;plugins&gt;
* &lt;plugin&gt;
* &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
* &lt;artifactId&gt;maven-javadoc-plugin&lt;/artifactId&gt;
* &lt;configuration&gt;
* &lt;helpfile&gt;path/to/your/resource/yourhelp-doc.html&lt;/helpfile&gt;
* ...
* &lt;/configuration&gt;
* &lt;dependencies&gt;
* &lt;dependency&gt;
* &lt;groupId&gt;groupId&lt;/groupId&gt;
* &lt;artifactId&gt;artifactId&lt;/artifactId&gt;
* &lt;version&gt;version&lt;/version&gt;
* &lt;/dependency&gt;
* &lt;/dependencies&gt;
* &lt;/plugin&gt;
* ...
* &lt;plugins&gt;
* &lt;/build&gt;
* </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>
* &lt;links&gt;
* &lt;link&gt;http://docs.oracle.com/javase/1.4.2/docs/api&lt;/link&gt;
* &lt;links&gt;
* </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 &lt;helpfile/&gt;.
* <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 &lt;splitindex/&gt;.
* <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 &lt;overview/&gt;.
* <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>
* &lt;noqualifier&gt;all&lt;/noqualifier&gt;
* or
* &lt;noqualifier&gt;packagename1:packagename2&lt;/noqualifier&gt;
* </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>
* &lt;offlineLinks&gt;
* &lt;offlineLink&gt;
* &lt;url&gt;http://docs.oracle.com/javase/1.5.0/docs/api/&lt;/url&gt;
* &lt;location&gt;../javadoc/jdk-5.0/&lt;/location&gt;
* &lt;/offlineLink&gt;
* &lt;/offlineLinks&gt;
* </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">d</a> option
*/
@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 &lt;noindex/&gt;.
* <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>
* &lt;stylesheetfile&gt;path/to/your/resource/yourstylesheet.css&lt;/stylesheetfile&gt;
* </pre>
* Where <code>path/to/your/resource/yourstylesheet.css</code> could be in <code>src/main/javadoc</code>.
* <pre>
* &lt;build&gt;
* &lt;plugins&gt;
* &lt;plugin&gt;
* &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
* &lt;artifactId&gt;maven-javadoc-plugin&lt;/artifactId&gt;
* &lt;configuration&gt;
* &lt;stylesheetfile&gt;path/to/your/resource/yourstylesheet.css&lt;/stylesheetfile&gt;
* ...
* &lt;/configuration&gt;
* &lt;dependencies&gt;
* &lt;dependency&gt;
* &lt;groupId&gt;groupId&lt;/groupId&gt;
* &lt;artifactId&gt;artifactId&lt;/artifactId&gt;
* &lt;version&gt;version&lt;/version&gt;
* &lt;/dependency&gt;
* &lt;/dependencies&gt;
* &lt;/plugin&gt;
* ...
* &lt;plugins&gt;
* &lt;/build&gt;
* </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>
* &lt;taglets&gt;
* &lt;taglet&gt;
* &lt;tagletClass&gt;com.sun.tools.doclets.ToDoTaglet&lt;/tagletClass&gt;
* &lt;/taglet&gt;
* &lt;taglet&gt;
* &lt;tagletClass&gt;package.to.AnotherTagletClass&lt;/tagletClass&gt;
* &lt;/taglet&gt;
* ...
* &lt;/taglets&gt;
* &lt;tagletArtifact&gt;
* &lt;groupId&gt;group-Taglet&lt;/groupId&gt;
* &lt;artifactId&gt;artifact-Taglet&lt;/artifactId&gt;
* &lt;version&gt;version-Taglet&lt;/version&gt;
* &lt;/tagletArtifact&gt;
* </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>
* &lt;tagletArtifacts&gt;
* &lt;tagletArtifact&gt;
* &lt;groupId&gt;group-Taglet&lt;/groupId&gt;
* &lt;artifactId&gt;artifact-Taglet&lt;/artifactId&gt;
* &lt;version&gt;version-Taglet&lt;/version&gt;
* &lt;/tagletArtifact&gt;
* ...
* &lt;/tagletArtifacts&gt;
* </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>
* &lt;taglets&gt;
* &lt;taglet&gt;
* &lt;tagletClass&gt;com.sun.tools.doclets.ToDoTaglet&lt;/tagletClass&gt;
* &lt;!--&lt;tagletpath&gt;/home/taglets&lt;/tagletpath&gt;--&gt;
* &lt;tagletArtifact&gt;
* &lt;groupId&gt;group-Taglet&lt;/groupId&gt;
* &lt;artifactId&gt;artifact-Taglet&lt;/artifactId&gt;
* &lt;version&gt;version-Taglet&lt;/version&gt;
* &lt;/tagletArtifact&gt;
* &lt;/taglet&gt;
* &lt;/taglets&gt;
* </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>
* &lt;tags&gt;
* &lt;tag&gt;
* &lt;name&gt;todo&lt;/name&gt;
* &lt;placement&gt;a&lt;/placement&gt;
* &lt;head&gt;To Do:&lt;/head&gt;
* &lt;/tag&gt;
* &lt;/tags&gt;
* </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
*/
@Parameter( defaultValue = "false" )
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>
* &lt;additionalDependencies&gt;
* &lt;additionalDependency&gt;
* &lt;groupId&gt;geronimo-spec&lt;/groupId&gt;
* &lt;artifactId&gt;geronimo-spec-jta&lt;/artifactId&gt;
* &lt;version&gt;1.0.1B-rc4:&lt;/version&gt;
* &lt;/additionalDependency&gt;
* &lt;/additionalDependencies&gt;
* </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.
*/
@Parameter
private List<String> sourceFileIncludes;
/**
* exclude filters on the source files.
* These are ignored if you specify subpackages or subpackage excludes.
*/
@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;
// ----------------------------------------------------------------------
// static
// ----------------------------------------------------------------------
static
{
DEFAULT_JAVA_API_LINKS.put( "api_1.5", "https://docs.oracle.com/javase/1.5.0/docs/api/" );
DEFAULT_JAVA_API_LINKS.put( "api_1.6", "https://docs.oracle.com/javase/6/docs/api/" );
DEFAULT_JAVA_API_LINKS.put( "api_1.7", "https://docs.oracle.com/javase/7/docs/api/" );
DEFAULT_JAVA_API_LINKS.put( "api_1.8", "https://docs.oracle.com/javase/8/docs/api/" );
DEFAULT_JAVA_API_LINKS.put( "api_9", "https://docs.oracle.com/javase/9/docs/api/" );
DEFAULT_JAVA_API_LINKS.put( "api_10", "https://docs.oracle.com/javase/10/docs/api/" );
DEFAULT_JAVA_API_LINKS.put( "api_11", "https://docs.oracle.com/en/java/javase/11/docs/api" );
}
// ----------------------------------------------------------------------
// 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() ) );
}
/**
* @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<String>> sourcePaths = getSourcePaths();
Collection<String> collectedSourcePaths = collect( sourcePaths.values() );
List<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( collectedSourcePaths, files );
}
List<String> filesWithUnnamedPackages = getFilesWithUnnamedPackages( collectedSourcePaths, 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 );
}
}
List<String> arguments = new ArrayList<>();
// ----------------------------------------------------------------------
// Wrap Javadoc options
// ----------------------------------------------------------------------
addJavadocOptions( javadocOutputDirectory, arguments, sourcePaths );
// ----------------------------------------------------------------------
// Wrap Standard doclet Options
// ----------------------------------------------------------------------
if ( StringUtils.isEmpty( doclet ) || useStandardDocletOptions )
{
addStandardDocletOptions( javadocOutputDirectory, arguments );
}
// ----------------------------------------------------------------------
// Write options file and include it in the command line
// ----------------------------------------------------------------------
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
// ----------------------------------------------------------------------
if ( !filesWithUnnamedPackages.isEmpty() )
{
addCommandLineArgFile( cmd, javadocOutputDirectory, filesWithUnnamedPackages );
}
}
else
{
// ----------------------------------------------------------------------
// Write argfile file and include it in the command line
// ----------------------------------------------------------------------
if ( !files.isEmpty() )
{
addCommandLineArgFile( cmd, javadocOutputDirectory, files );
}
}
// ----------------------------------------------------------------------
// 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 Collection<String> collect( Collection<Collection<String>> sourcePaths )
{
Collection<String> collectedSourcePaths = new LinkedHashSet<>();
for ( Collection<String> 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 List<String> getFiles( Collection<String> sourcePaths )
throws MavenReportException
{
List<String> files = new ArrayList<>();
if ( StringUtils.isEmpty( subpackages ) )
{
Collection<String> excludedPackages = getExcludedPackages();
for ( String sourcePath : sourcePaths )
{
File sourceDirectory = new File( sourcePath );
files.addAll( JavadocUtil.getFilesFromSource( sourceDirectory, sourceFileIncludes, sourceFileExcludes,
excludedPackages ) );
}
}
return files;
}
/**
* Method to get the source paths. If no source path is specified in the parameter, the compile source roots
* of the project will be used.
*
* @return a Collection of the project absolute source paths as <code>String</code>
* @throws MavenReportException {@link MavenReportException}
* @see JavadocUtil#pruneDirs(MavenProject, Collection)
*/
protected Map<String, Collection<String>> getSourcePaths()
throws MavenReportException
{
Map<String, Collection<String>> mappedSourcePaths = new LinkedHashMap<>();
if ( StringUtils.isEmpty( sourcepath ) )
{
Set<String> 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<String> l = JavadocUtil.pruneDirs( project, Collections.singletonList(
getJavadocDirectory().getAbsolutePath() ) );
sourcePaths.addAll( l );
}
}
mappedSourcePaths.put( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
sourcePaths );
if ( includeDependencySources )
{
mappedSourcePaths.putAll( getDependencySourcePaths() );
}
if ( isAggregator() )
{
for ( MavenProject subProject : getAggregatedProjects() )
{
if ( subProject != project )
{
Collection<String> 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<String> l = JavadocUtil.pruneDirs( subProject, Collections.singletonList(
javadocDir.getAbsolutePath() ) );
additionalSourcePaths.addAll( l );
}
}
mappedSourcePaths.put( ArtifactUtils.versionlessKey( subProject.getGroupId(),
subProject.getArtifactId() ),
additionalSourcePaths );
}
}
}
}
else
{
Collection<String> sourcePaths = new ArrayList<>( Arrays.asList( JavadocUtil.splitPath( sourcepath ) ) );
sourcePaths = JavadocUtil.pruneDirs( project, sourcePaths );
if ( getJavadocDirectory() != null )
{
Collection<String> 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<Path>();
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<String>> 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();
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 ) );
}
config.withFilter( new AndFilter( andFilters ) );
}
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()
{
return configureDependencySourceResolution(
new SourceResolverConfig( project, session.getProjectBuildingRequest(),
sourceDependencyCacheDir ).withReactorProjects( reactorProjects ) );
}
/**
* 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( List<String> files )
{
boolean canGenerate = true;
if ( files.isEmpty() && StringUtils.isEmpty( subpackages ) )
{
canGenerate = false;
}
return canGenerate;
}
// ----------------------------------------------------------------------
// 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<String> sourceFolders )
throws MavenReportException
{
List<String> excludedNames = null;
Collection<Path> sourcePaths = new ArrayList<>( sourceFolders.size() );
for ( String folder : sourceFolders )
{
sourcePaths.add( Paths.get( folder ) );
}
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<String> 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 List<File> getPathElements()
throws MavenReportException
{
List<File> classpathElements = new ArrayList<>();
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 )
{
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 = session.getProjectBuildingRequest();
buildingRequest =
buildingRequest.setRemoteRepositories( subProject.getRemoteArtifactRepositories() );
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( session.getProjectBuildingRequest(), 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}&#x2013;", "" );
}
else
{
theBottom = StringUtils.replace( theBottom, "{inceptionYear}", inceptionYear );
}
}
else
{
theBottom = StringUtils.replace( theBottom, "{inceptionYear}&#x2013;", "" );
}
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( getArtifactsAbsolutePath( taglet.getTagletArtifact() ) );
pathParts = JavadocUtil.pruneFiles( pathParts );
}
else if ( StringUtils.isNotEmpty( taglet.getTagletpath() ) )
{
pathParts.add( taglet.getTagletpath() );
pathParts = JavadocUtil.pruneDirs( project, pathParts );
}
}
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( session.getProjectBuildingRequest(), 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() );
DefaultProjectBuildingRequest buildingRequest =
new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
buildingRequest.setRemoteRepositories( project.getRemoteArtifactRepositories() );
return artifactResolver.resolveArtifact( buildingRequest, 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.getActiveProxy() == null )
{
return;
}
Proxy activeProxy = settings.getActiveProxy();
String protocol = StringUtils.isNotEmpty( activeProxy.getProtocol() ) ? activeProxy.getProtocol() + "." : "";
if ( StringUtils.isNotEmpty( activeProxy.getHost() ) )
{
cmd.createArg().setValue( "-J-D" + protocol + "proxySet=true" );
cmd.createArg().setValue( "-J-D" + protocol + "proxyHost=" + activeProxy.getHost() );
if ( activeProxy.getPort() > 0 )
{
cmd.createArg().setValue( "-J-D" + protocol + "proxyPort=" + activeProxy.getPort() );
}
if ( StringUtils.isNotEmpty( activeProxy.getNonProxyHosts() ) )
{
cmd.createArg().setValue(
"-J-D" + protocol + "nonProxyHosts=\"" + activeProxy.getNonProxyHosts() + "\"" );
}
if ( StringUtils.isNotEmpty( activeProxy.getUsername() ) )
{
cmd.createArg().setValue( "-J-Dhttp.proxyUser=\"" + activeProxy.getUsername() + "\"" );
if ( StringUtils.isNotEmpty( activeProxy.getPassword() ) )
{
cmd.createArg().setValue( "-J-Dhttp.proxyPassword=\"" + activeProxy.getPassword() + "\"" );
}
}
}
}
/**
* 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 )
throws MavenReportException
{
Set<OfflineLink> offlineLinksList = collectOfflineLinks();
offlineLinksList.addAll( getModulesLinks() );
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 );
}
}
}
/**
* 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
* @param files not null
* @return the list of package names for files in the sourcePaths
*/
private List<String> getPackageNames( Collection<String> sourcePaths, List<String> files )
{
return getPackageNamesOrFilesWithUnnamedPackages( sourcePaths, files, true );
}
/**
* @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<String>> allSourcePaths )
throws MavenReportException
{
List<String> returnList = new ArrayList<>();
if ( !StringUtils.isEmpty( sourcepath ) )
{
return returnList;
}
LocationManager locationManager = new LocationManager();
for ( Collection<String> artifactSourcePaths: allSourcePaths.values() )
{
Set<String> exportedPackages = new HashSet<>();
boolean exportAllPackages;
File mainDescriptor = findMainDescriptor( artifactSourcePaths );
if ( mainDescriptor != null && !isTest() )
{
ResolvePathsRequest<File> request =
ResolvePathsRequest.withFiles( 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 ( String currentFile : getFiles( artifactSourcePaths ) )
{
currentFile = currentFile.replace( '\\', '/' );
for ( String currentSourcePath : artifactSourcePaths )
{
currentSourcePath = currentSourcePath.replace( '\\', '/' );
if ( currentFile.contains( currentSourcePath ) )
{
if ( !currentSourcePath.endsWith( "/" ) )
{
currentSourcePath += "/";
}
String packagename = currentFile.substring( currentSourcePath.length() + 1 );
/*
* Remove the miscellaneous files
* http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
*/
if ( packagename.contains( "doc-files" ) )
{
continue;
}
if ( packagename.lastIndexOf( "/" ) != -1 )
{
packagename = packagename.substring( 0, packagename.lastIndexOf( "/" ) );
packagename = packagename.replace( '/', '.' );
if ( exportAllPackages || exportedPackages.contains( packagename ) )
{
returnList.add( packagename );
}
}
}
}
}
}
return returnList;
}
/**
* @param sourcePaths could be null
* @param files not null
* @return a list files with unnamed package names for files in the sourcePaths
*/
private List<String> getFilesWithUnnamedPackages( Collection<String> sourcePaths, List<String> files )
{
return getPackageNamesOrFilesWithUnnamedPackages( sourcePaths, files, false );
}
/**
* @param sourcePaths not null, containing absolute and relative paths
* @param files not null, containing list of quoted files
* @param onlyPackageName boolean for only package name
* @return a list of package names or files with unnamed package names, depending the value of the unnamed flag
* @see #getFiles
* @see #getSourcePaths()
*/
private List<String> getPackageNamesOrFilesWithUnnamedPackages( Collection<String> sourcePaths, List<String> files,
boolean onlyPackageName )
{
List<String> returnList = new ArrayList<>();
if ( !StringUtils.isEmpty( sourcepath ) )
{
return returnList;
}
for ( String currentFile : files )
{
currentFile = currentFile.replace( '\\', '/' );
for ( String currentSourcePath : sourcePaths )
{
currentSourcePath = currentSourcePath.replace( '\\', '/' );
if ( !currentSourcePath.endsWith( "/" ) )
{
currentSourcePath += "/";
}
if ( currentFile.contains( currentSourcePath ) )
{
String packagename = currentFile.substring( currentSourcePath.length() + 1 );
/*
* Remove the miscellaneous files
* http://docs.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#unprocessed
*/
if ( packagename.contains( "doc-files" ) )
{
continue;
}
if ( onlyPackageName && packagename.lastIndexOf( "/" ) != -1 )
{
packagename = packagename.substring( 0, packagename.lastIndexOf( "/" ) );
packagename = packagename.replace( '/', '.' );
if ( !returnList.contains( packagename ) )
{
returnList.add( packagename );
}
}
if ( !onlyPackageName && packagename.lastIndexOf( "/" ) == -1 )
{
returnList.add( currentFile );
}
}
}
}
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 );
}
try
{
FileUtils.fileWrite( argfileFile.getAbsolutePath(), null /* platform encoding */,
StringUtils.join( files.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 &lt;nohelp/&gt; and &lt;helpfile/&gt; 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." );
}
// default java api links
if ( javaApiLinks == null || javaApiLinks.size() == 0 )
{
javaApiLinks = DEFAULT_JAVA_API_LINKS;
}
}
/**
* 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<String>> allSourcePaths )
throws MavenReportException
{
Collection<String> 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 );
}
Collection<String> reactorKeys = new HashSet<>( session.getProjects().size() );
for ( MavenProject reactorProject : session.getProjects() )
{
reactorKeys.add( ArtifactUtils.versionlessKey( reactorProject.getGroupId(),
reactorProject.getArtifactId() ) );
}
final LocationManager locationManager = new LocationManager();
Collection<String> additionalModules = new ArrayList<>();
Path moduleSourceDir = null;
if ( allSourcePaths.size() > 1 )
{
for ( Map.Entry<String, Collection<String>> projectSourcepaths : allSourcePaths.entrySet() )
{
if ( reactorKeys.contains( projectSourcepaths.getKey() ) )
{
File moduleDescriptor = findMainDescriptor( projectSourcepaths.getValue() );
if ( moduleDescriptor != null )
{
moduleSourceDir = javadocOutputDirectory.toPath().resolve( "src" );
try
{
moduleSourceDir = Files.createDirectories( moduleSourceDir );
ResolvePathsRequest<File> request =
ResolvePathsRequest.withFiles( Collections.<File>emptyList() )
.setMainModuleDescriptor( moduleDescriptor );
String moduleName =
locationManager.resolvePaths( request ).getMainModuleDescriptor().name();
additionalModules.add( moduleName );
addArgIfNotEmpty( arguments, "--patch-module", moduleName + '='
+ JavadocUtil.quotedPathArgument( getSourcePath( projectSourcepaths.getValue() ) ) );
Path modulePath = moduleSourceDir.resolve( moduleName );
if ( !Files.isDirectory( modulePath ) )
{
Files.createDirectory( modulePath );
}
}
catch ( IOException e )
{
throw new MavenReportException( e.getMessage(), e );
}
}
else
{
// todo
getLog().error( "no module descriptor for " + projectSourcepaths.getKey() );
}
}
else
{
// todo
getLog().error( "no reactor project: " + projectSourcepaths.getKey() );
}
}
}
List<String> roots = getProjectSourceRoots( getProject() );
File mainDescriptor = findMainDescriptor( roots );
if ( javadocRuntimeVersion.isAtLeast( "9" ) && ( isAggregator() || mainDescriptor != null ) && !isTest() )
{
ResolvePathsRequest<File> request =
ResolvePathsRequest.withFiles( getPathElements() ).setAdditionalModules( additionalModules );
if ( mainDescriptor != null )
{
request.setMainModuleDescriptor( mainDescriptor );
}
try
{
ResolvePathsResult<File> result = locationManager.resolvePaths( request );
Set<File> modulePathElements = new HashSet<>( result.getModulepathElements().keySet() ) ;
if ( allSourcePaths.size() > 1 )
{
// Probably required due to bug in javadoc (Java 9+)
modulePathElements.addAll( getProjectBuildOutputDirs( getProject() ) );
}
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
{
classPathElements.add( file );
}
}
String classpath = StringUtils.join( classPathElements.iterator(), File.pathSeparator );
addArgIfNotEmpty( arguments, "--class-path", JavadocUtil.quotedPathArgument( classpath ) );
String modulepath =
StringUtils.join( modulePathElements.iterator(), File.pathSeparator );
addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ) );
}
catch ( IOException e )
{
throw new MavenReportException( e.getMessage(), e );
}
}
else
{
String classpath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
addArgIfNotEmpty( arguments, "-classpath", JavadocUtil.quotedPathArgument( classpath ) );
}
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 );
}
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, "--module-source-path",
JavadocUtil.quotedPathArgument( moduleSourceDir.toString() ) );
}
else
{
addArgIfNotEmpty( arguments, "-sourcepath",
JavadocUtil.quotedPathArgument( getSourcePath( sourcePaths ) ) );
}
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 )
{
Collections.addAll( arguments, additionalOptions );
}
}
private File findMainDescriptor( Collection<String> roots )
{
for ( String root : roots )
{
File descriptorFile = new File( root, "module-info.java" ).getAbsoluteFile();
if ( descriptorFile.exists() )
{
return descriptorFile;
}
}
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 )
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 );
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(), ",", "&#44;" );
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 ( getLog().isDebugEnabled() )
{
// no quoted arguments
getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) );
}
String cmdLine = null;
if ( debug )
{
cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
cmdLine = JavadocUtil.hideProxyPassword( cmdLine, settings );
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( "'", "" );
cmdLine = JavadocUtil.hideProxyPassword( cmdLine, settings );
}
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, session.getProjectBuildingRequest() ).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 #DEFAULT_JAVA_API_LINKS
* @see <a href="http://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source">source parameter</a>
* @since 2.6
*/
private OfflineLink getDefaultJavadocApiLink()
{
if ( !detectJavaApiLink )
{
return null;
}
final String pluginId = "org.apache.maven.plugins:maven-compiler-plugin";
JavaVersion sourceVersion = javadocRuntimeVersion;
String sourceConfigured = getPluginParameter( project, pluginId, "source" );
if ( sourceConfigured != null )
{
try
{
sourceVersion = JavaVersion.parse( sourceConfigured );
}
catch ( NumberFormatException e )
{
getLog().debug(
"NumberFormatException for the source parameter in the maven-compiler-plugin. " + "Ignored it", e );
}
}
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 );
}
String apiVersion;
Matcher apiMatcher = Pattern.compile( "(1\\.\\d|\\d\\d*)" ).matcher( sourceVersion.toString() );
if ( apiMatcher.find() )
{
apiVersion = apiMatcher.group( 1 );
}
else
{
apiVersion = null;
}
String javaApiLink = javaApiLinks.getProperty( "api_" + apiVersion, null );
if ( getLog().isDebugEnabled() )
{
if ( StringUtils.isNotEmpty( javaApiLink ) )
{
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 ( JavaVersion.parse( apiVersion ).isAtLeast( "10" ) )
{
javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "element-list" );
resourceName = "java-api-element-list-" + apiVersion;
}
else
{
javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
resourceName = "java-api-package-list-" + apiVersion;
}
OfflineLink link = new OfflineLink();
link.setLocation( javaApiListFile.getParent().toAbsolutePath().toString() );
link.setUrl( javaApiLink );
try ( InputStream in = this.getClass().getResourceAsStream( resourceName ) )
{
// TODO only copy when changed
Files.copy( in, javaApiListFile, StandardCopyOption.REPLACE_EXISTING );
}
catch ( IOException ioe )
{
logError( "Can't get java-api-package-list-" + apiVersion + ": " + 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() );
}
}
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 );
}
}