All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugin.jxr.AbstractJxrReport Maven / Gradle / Ivy

The newest version!
/*
 * 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.
 */
package org.apache.maven.plugin.jxr;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.jxr.JXR;
import org.apache.maven.jxr.JavaCodeTransform;
import org.apache.maven.jxr.JxrException;
import org.apache.maven.jxr.pacman.FileManager;
import org.apache.maven.jxr.pacman.PackageManager;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.AbstractMavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.languages.java.version.JavaVersion;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;

/**
 * Base class for the JXR reports.
 *
 * @author Fabrice Bellingard
 * @author Brett Porter
 * @author Vincent Siveton
 */
public abstract class AbstractJxrReport extends AbstractMavenReport {

    @Parameter(defaultValue = "${session}", readonly = true, required = true)
    private MavenSession session;

    /**
     * Title of window of the Xref HTML files.
     */
    @Parameter(defaultValue = "${project.name} ${project.version} Reference")
    private String windowTitle;

    /**
     * Title of main page of the Xref HTML files.
     */
    @Parameter(defaultValue = "${project.name} ${project.version} Reference")
    private String docTitle;

    // CHECKSTYLE_OFF: LineLength
    /**
     * String used at the bottom of the Xref HTML files.
     */
    @Parameter(property = "bottom", defaultValue = "\u00A9 {inceptionYear}\u2013{currentYear} {organizationName}")
    private String bottom;

    // CHECKSTYLE_ON: LineLength

    /**
     * Directory where Velocity templates can be found to generate overviews, frames and summaries. Should not be used.
     * If used, should be an absolute path, like {@literal "${basedir}/myTemplates"}.
     */
    @Parameter
    private String templateDir;

    /**
     * Style sheet used for the Xref HTML files. Should not be used. If used, should be an absolute path, like
     * {@literal "${basedir}/myStyles.css"}.
     */
    @Parameter
    private String stylesheet;

    /**
     * A list of exclude patterns to use. By default no files are excluded.
     *
     * @since 2.1
     */
    @Parameter
    private ArrayList excludes;

    /**
     * A list of include patterns to use. By default all .java files are included.
     *
     * @since 2.1
     */
    @Parameter
    private ArrayList includes;

    /**
     * Whether to skip this execution.
     *
     * @since 2.3
     */
    @Parameter(property = "maven.jxr.skip", defaultValue = "false")
    protected boolean skip;

    /**
     * Link the Javadoc from the Source XRef. Defaults to true and will link automatically if javadoc plugin is being
     * used.
     */
    @Parameter(defaultValue = "true")
    private boolean linkJavadoc;

    /**
     * Version of the Javadoc templates to use.
     * The value should reflect `java.specification.version`, "1.4", "1.8", "9", "10",
     * by default this system property is used.
     */
    @Parameter(property = "javadocVersion")
    private String javadocVersion;

    /**
     * Version of the Javadoc templates to use.
     */
    private JavaVersion javadocTemplatesVersion;

    /**
     * Compiles the list of directories which contain source files that will be included in the JXR report generation.
     *
     * @param sourceDirs the List of the source directories
     * @return a List of the directories that will be included in the JXR report generation
     */
    protected List pruneSourceDirs(List sourceDirs) {
        List pruned = new ArrayList<>(sourceDirs.size());
        for (String dir : sourceDirs) {
            if (!pruned.contains(dir) && hasSources(new File(dir))) {
                pruned.add(dir);
            }
        }
        return pruned;
    }

    /**
     * Initialize some attributes required during the report generation
     */
    protected void init() {
        // wanna know if Javadoc is being generated
        // TODO: what if it is not part of the site though, and just on the command line?
        if (project.getModel().getReporting() != null) {
            for (ReportPlugin reportPlugin : Collections.unmodifiableList(
                    project.getModel().getReporting().getPlugins())) {
                if ("maven-javadoc-plugin".equals(reportPlugin.getArtifactId())) {
                    break;
                }
            }
        }
    }

    /**
     * Checks whether the given directory contains Java files.
     *
     * @param dir the source directory
     * @return true if the directory or one of its subdirectories contains at least 1 Java file
     */
    private boolean hasSources(File dir) {
        if (dir.exists() && dir.isDirectory()) {
            for (File currentFile : dir.listFiles()) {
                if (currentFile.isFile()) {
                    if (currentFile.getName().endsWith(".java")) {
                        return true;
                    }
                } else {
                    if (Character.isJavaIdentifierStart(currentFile.getName().charAt(0)) // avoid .svn directory
                            && hasSources(currentFile)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Creates the Xref for the Java files found in the given source directory and puts them in the given output
     * directory.
     *
     * @param locale The user locale to use for the Xref generation
     * @param outputDirectory The output directory
     * @param sourceDirs The source directories
     * @throws java.io.IOException
     * @throws org.apache.maven.jxr.JxrException
     */
    private void createXref(Locale locale, File outputDirectory, List sourceDirs)
            throws IOException, JxrException {
        FileManager fileManager = new FileManager();
        PackageManager packageManager = new PackageManager(fileManager);
        JavaCodeTransform codeTransform = new JavaCodeTransform(packageManager, fileManager);

        JXR jxr = new JXR(packageManager, codeTransform);
        jxr.setDest(outputDirectory.toPath());
        jxr.setInputEncoding(getInputEncoding());
        jxr.setLocale(locale);
        jxr.setOutputEncoding(getOutputEncoding());
        jxr.setRevision("HEAD");
        jxr.setJavadocLinkDir(constructJavadocLocation());
        // Set include/exclude patterns on the jxr instance
        if (excludes != null && !excludes.isEmpty()) {
            jxr.setExcludes(excludes.toArray(new String[0]));
        }
        if (includes != null && !includes.isEmpty()) {
            jxr.setIncludes(includes.toArray(new String[0]));
        }

        // avoid winding up using Velocity in two class loaders.
        ClassLoader savedTccl = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
            jxr.xref(sourceDirs, getTemplateDir(), windowTitle, docTitle, getBottomText());
        } finally {
            Thread.currentThread().setContextClassLoader(savedTccl);
        }

        // and finally copy the stylesheet
        copyRequiredResources(outputDirectory);
    }

    /**
     * Returns the bottom text to be displayed at the lower part of the generated JXR report.
     */
    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}\u2013", "");
            } else {
                theBottom = StringUtils.replace(theBottom, "{inceptionYear}", inceptionYear);
            }
        } else {
            theBottom = StringUtils.replace(theBottom, "{inceptionYear}\u2013", "");
        }

        if (project.getOrganization() == null) {
            theBottom = StringUtils.replace(theBottom, " {organizationName}", "");
        } else {
            if (StringUtils.isNotEmpty(project.getOrganization().getName())) {
                if (StringUtils.isNotEmpty(project.getOrganization().getUrl())) {
                    // CHECKSTYLE_OFF: LineLength
                    theBottom = StringUtils.replace(
                            theBottom,
                            "{organizationName}",
                            ""
                                    + project.getOrganization().getName() + "");
                    // CHECKSTYLE_ON: LineLength
                } else {
                    theBottom = StringUtils.replace(
                            theBottom,
                            "{organizationName}",
                            project.getOrganization().getName());
                }
            } else {
                theBottom = StringUtils.replace(theBottom, " {organizationName}", "");
            }
        }

        return theBottom;
    }

    /**
     * Copy some required resources (like the stylesheet) to the given target directory
     *
     * @param targetDirectory the directory to copy the resources to
     */
    private void copyRequiredResources(File targetDirectory) {
        if (stylesheet != null && !stylesheet.isEmpty()) {
            File stylesheetFile = new File(stylesheet);
            File targetStylesheetFile = new File(targetDirectory, "stylesheet.css");

            try {
                if (stylesheetFile.isAbsolute()) {
                    FileUtils.copyFile(stylesheetFile, targetStylesheetFile);
                } else {
                    URL stylesheetUrl = this.getClass().getClassLoader().getResource(stylesheet);
                    FileUtils.copyURLToFile(stylesheetUrl, targetStylesheetFile);
                }
            } catch (IOException e) {
                getLog().warn("An error occured while copying the stylesheet to the target directory", e);
            }
        } else {
            if (javadocTemplatesVersion.isAtLeast("1.8")) {
                copyResources(targetDirectory, "jdk8/", "stylesheet.css");
            } else if (javadocTemplatesVersion.isAtLeast("1.7")) {
                String[] jdk7Resources = {
                    "stylesheet.css",
                    "resources/background.gif",
                    "resources/tab.gif",
                    "resources/titlebar.gif",
                    "resources/titlebar_end.gif"
                };
                copyResources(targetDirectory, "jdk7/", jdk7Resources);
            } else if (javadocTemplatesVersion.isAtLeast("1.6")) {
                copyResources(targetDirectory, "jdk6/", "stylesheet.css");
            } else if (javadocTemplatesVersion.isAtLeast("1.4")) {
                copyResources(targetDirectory, "jdk4/", "stylesheet.css");
            } else {
                // Fallback to the original stylesheet
                copyResources(targetDirectory, "", "stylesheet.css");
            }
        }
    }

    /**
     * Copy styles and related resources to the given directory
     *
     * @param targetDirectory the target directory to copy the resources to
     * @param sourceDirectory resources subdirectory to copy from
     * @param files names of files to copy
     */
    private void copyResources(File targetDirectory, String sourceDirectory, String... files) {
        try {
            for (String file : files) {
                URL resourceUrl = this.getClass().getClassLoader().getResource(sourceDirectory + file);
                File targetResourceFile = new File(targetDirectory, file);
                FileUtils.copyURLToFile(resourceUrl, targetResourceFile);
            }
        } catch (IOException e) {
            getLog().warn("An error occured while copying the resource to the target directory", e);
        }
    }

    @Override
    protected MavenProject getProject() {
        return project;
    }

    protected MavenSession getSession() {
        return session;
    }

    protected List getReactorProjects() {
        return reactorProjects;
    }

    protected MojoExecution getMojoExecution() {
        return mojoExecution;
    }

    /**
     * Returns the correct resource bundle according to the locale.
     *
     * @param locale the locale of the user
     * @return the bundle corresponding to the locale
     */
    protected ResourceBundle getBundle(Locale locale) {
        return ResourceBundle.getBundle("jxr-report", locale, this.getClass().getClassLoader());
    }

    @Override
    protected void executeReport(Locale locale) throws MavenReportException {
        // init some attributes -- TODO (javadoc)
        init();

        // determine version of templates to use
        setJavadocTemplatesVersion();

        try {
            createXref(locale, getPluginReportOutputDirectory(), constructSourceDirs());
        } catch (JxrException | IOException e) {
            throw new MavenReportException("Error while generating the HTML source code of the project.", e);
        }
    }

    /**
     * Determine the templateDir to use, given javadocTemplatesVersion
     *
     * @return
     */
    private String getTemplateDir() {
        // Check if overridden
        if (templateDir == null || templateDir.isEmpty()) {
            if (javadocTemplatesVersion.isAtLeast("1.8")) {
                return "templates/jdk8";
            } else if (javadocTemplatesVersion.isAtLeast("1.7")) {
                return "templates/jdk7";
            } else if (javadocTemplatesVersion.isAtLeast("1.4")) {
                return "templates/jdk4";
            } else {
                getLog().warn("Unsupported javadocVersion: " + javadocTemplatesVersion + ". Fallback to original");
                return "templates";
            }
        }
        // use value specified by user
        return templateDir;
    }

    /**
     * Sets a new value for {@code javadocTemplatesVersion}.
     */
    private void setJavadocTemplatesVersion() {
        JavaVersion javaVersion = JavaVersion.JAVA_SPECIFICATION_VERSION;

        if (javadocVersion != null && !javadocVersion.isEmpty()) {
            javadocTemplatesVersion = JavaVersion.parse(javadocVersion);
        } else {
            javadocTemplatesVersion = javaVersion;
        }
    }

    /**
     * Gets the list of the source directories to be included in the JXR report generation
     *
     * @return a List of the source directories whose contents will be included in the JXR report generation
     */
    protected List constructSourceDirs() {
        List sourceDirs = new ArrayList<>(getSourceRoots());
        if (isAggregate()) {
            for (MavenProject project : reactorProjects) {
                if ("java".equals(project.getArtifact().getArtifactHandler().getLanguage())) {
                    sourceDirs.addAll(getSourceRoots(project));
                }
            }
        }

        sourceDirs = pruneSourceDirs(sourceDirs);
        return sourceDirs;
    }

    @Override
    public boolean canGenerateReport() {
        if (skip) {
            return false;
        }

        if (constructSourceDirs().isEmpty()) {
            return false;
        }

        if (isAggregate() && !project.isExecutionRoot()) {
            return false;
        }

        return true;
    }

    @Override
    public boolean isExternalReport() {
        return true;
    }

    /**
     * @return a String that contains the location of the javadocs
     */
    private Path constructJavadocLocation() throws IOException {
        Path location = null;
        if (linkJavadoc) {
            // We don't need to do the whole translation thing like normal, because JXR does it internally.
            // It probably shouldn't.
            if (getJavadocLocation().exists()) {
                // XRef was already generated by manual execution of a lifecycle binding
                location = getJavadocLocation().toPath().toAbsolutePath();
            } else {
                // Not yet generated - check if the report is on its way

                // Special case: using the site:stage goal
                String stagingDirectory = System.getProperty("stagingDirectory");

                if (stagingDirectory != null && !stagingDirectory.isEmpty()) {
                    String javadocOutputDir = getJavadocLocation().getName();
                    boolean javadocAggregate = JxrReportUtil.isJavadocAggregated(project);
                    String structureProject = JxrReportUtil.getStructure(project, false);

                    if (isAggregate() && javadocAggregate) {
                        location = Paths.get(stagingDirectory, structureProject, javadocOutputDir);
                    }
                    if (!isAggregate() && javadocAggregate) {
                        location = Paths.get(stagingDirectory, javadocOutputDir);

                        String hierarchy = project.getName();

                        MavenProject parent = project.getParent();
                        while (parent != null) {
                            hierarchy = parent.getName();
                            parent = parent.getParent();
                        }
                        location = Paths.get(stagingDirectory, hierarchy, javadocOutputDir);
                    }
                    if (isAggregate() && !javadocAggregate) {
                        getLog().warn("The JXR plugin is configured to build an aggregated report at the root, "
                                + "not the Javadoc plugin.");
                    }
                    if (!isAggregate() && !javadocAggregate) {
                        location = Paths.get(stagingDirectory, structureProject, javadocOutputDir);
                    }
                } else {
                    location = getJavadocLocation().toPath();
                }
            }

            if (location == null) {
                getLog().warn("Unable to locate Javadoc to link to - DISABLED");
            }
        }

        return location;
    }

    /**
     * Abstract method that returns the plugin report output directory where the generated JXR report will be put
     * beneath {@link #getReportOutputDirectory()}.
     *
     * @return a File for the plugin's report output directory
     */
    protected abstract File getPluginReportOutputDirectory();

    /**
     * Abstract method that returns the specified source directories that will be included in the JXR report generation.
     *
     * @return a List of the source directories
     */
    protected abstract List getSourceRoots();

    /**
     * Abstract method that returns the compile source directories of the specified project that will be included in the
     * JXR report generation
     *
     * @param project the MavenProject where the JXR report plugin will be executed
     * @return a List of the source directories
     */
    protected abstract List getSourceRoots(MavenProject project);

    /**
     * Abstract method that returns the location where (Test) Javadoc is generated for this project.
     *
     * @return a File for the location of (test) javadoc
     */
    protected abstract File getJavadocLocation();

    /**
     * Is the current report aggregated?
     *
     * @return true if aggregate, false otherwise
     */
    protected boolean isAggregate() {
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy