org.apache.maven.report.projectinfo.LicenseReport.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.maven.report.projectinfo.LicenseReport.java

Source

package org.apache.maven.report.projectinfo;

/*
 * 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.validator.routines.UrlValidator;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.util.HtmlTools;
import org.apache.maven.model.License;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.codehaus.plexus.i18n.I18N;
import org.codehaus.plexus.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Generates the Project License report.
 *
 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton </a>
 * @version $Id$
 * @since 2.0
 */
@Mojo(name = "license")
public class LicenseReport extends AbstractProjectInfoReport {
    // ----------------------------------------------------------------------
    // Mojo parameters
    // ----------------------------------------------------------------------

    /**
     * The Maven Settings.
     */
    @Component
    private Settings settings;

    /**
     * Whether the system is currently offline.
     */
    @Parameter(property = "settings.offline")
    private boolean offline;

    /**
     * Whether the only render links to the license documents instead of inlining them.
     * <br/>
     * If the system is in {@link #offline} mode, the linkOnly parameter will be always <code>true</code>.
     *
     * @since 2.3
     */
    @Parameter(defaultValue = "false")
    private boolean linkOnly;

    // ----------------------------------------------------------------------
    // Public methods
    // ----------------------------------------------------------------------

    @Override
    public void executeReport(Locale locale) {
        LicenseRenderer r = new LicenseRenderer(getSink(), getProject(), getI18N(locale), locale, settings,
                linkOnly);

        r.render();
    }

    @Override
    public boolean canGenerateReport() {
        if (!offline) {
            return true;
        }

        for (License license : project.getModel().getLicenses()) {
            String url = license.getUrl();

            URL licenseUrl = null;
            try {
                licenseUrl = getLicenseURL(project, url);
            } catch (MalformedURLException e) {
                getLog().error(e.getMessage());
            } catch (IOException e) {
                getLog().error(e.getMessage());
            }

            if (licenseUrl != null && licenseUrl.getProtocol().equals("file")) {
                return true;
            }

            if (licenseUrl != null
                    && (licenseUrl.getProtocol().equals("http") || licenseUrl.getProtocol().equals("https"))) {
                linkOnly = true;
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritDoc}
     */
    public String getOutputName() {
        return "license";
    }

    @Override
    protected String getI18Nsection() {
        return "license";
    }

    /**
     * @param project not null
     * @param url     not null
     * @return a valid URL object from the url string
     * @throws IOException if any
     */
    protected static URL getLicenseURL(MavenProject project, String url) throws IOException {
        URL licenseUrl;
        UrlValidator urlValidator = new UrlValidator(UrlValidator.ALLOW_ALL_SCHEMES);
        // UrlValidator does not accept file URLs because the file
        // URLs do not contain a valid authority (no hostname).
        // As a workaround accept license URLs that start with the
        // file scheme.
        if (urlValidator.isValid(url) || StringUtils.defaultString(url).startsWith("file://")) {
            try {
                licenseUrl = new URL(url);
            } catch (MalformedURLException e) {
                throw new MalformedURLException(
                        "The license url '" + url + "' seems to be invalid: " + e.getMessage());
            }
        } else {
            File licenseFile = new File(project.getBasedir(), url);
            if (!licenseFile.exists()) {
                // Workaround to allow absolute path names while
                // staying compatible with the way it was...
                licenseFile = new File(url);
            }
            if (!licenseFile.exists()) {
                throw new IOException("Maven can't find the file '" + licenseFile + "' on the system.");
            }
            try {
                licenseUrl = licenseFile.toURI().toURL();
            } catch (MalformedURLException e) {
                throw new MalformedURLException(
                        "The license url '" + url + "' seems to be invalid: " + e.getMessage());
            }
        }

        return licenseUrl;
    }

    // ----------------------------------------------------------------------
    // Private
    // ----------------------------------------------------------------------

    /**
     * Internal renderer class
     */
    private static class LicenseRenderer extends AbstractProjectInfoRenderer {
        private final MavenProject project;

        private final Settings settings;

        private final boolean linkOnly;

        LicenseRenderer(Sink sink, MavenProject project, I18N i18n, Locale locale, Settings settings,
                boolean linkOnly) {
            super(sink, i18n, locale);

            this.project = project;

            this.settings = settings;

            this.linkOnly = linkOnly;
        }

        @Override
        protected String getI18Nsection() {
            return "license";
        }

        @Override
        public void renderBody() {
            List<License> licenses = project.getModel().getLicenses();

            if (licenses.isEmpty()) {
                startSection(getTitle());

                paragraph(getI18nString("nolicense"));

                endSection();

                return;
            }

            // Overview
            startSection(getI18nString("overview.title"));

            paragraph(getI18nString("overview.intro"));

            endSection();

            // License
            startSection(getI18nString("title"));

            if (licenses.size() > 1) {
                // multiple licenses
                paragraph(getI18nString("multiple"));

                if (!linkOnly) {
                    // add an index before licenses content
                    sink.list();
                    for (License license : licenses) {
                        String name = license.getName();

                        sink.listItem();
                        link("#" + HtmlTools.encodeId(name), name);
                        sink.listItem_();
                    }
                    sink.list_();
                }
            }

            for (License license : licenses) {
                String name = license.getName();
                String url = license.getUrl();
                String comments = license.getComments();

                startSection(name);

                if (!StringUtils.isEmpty(comments)) {
                    paragraph(comments);
                }

                if (url != null) {
                    try {
                        URL licenseUrl = getLicenseURL(project, url);

                        if (linkOnly) {
                            link(licenseUrl.toExternalForm(), licenseUrl.toExternalForm());
                        } else {
                            renderLicenseContent(licenseUrl);
                        }
                    } catch (MalformedURLException e) {
                        // I18N message
                        paragraph(e.getMessage());
                    } catch (IOException e) {
                        // I18N message
                        paragraph(e.getMessage());
                    }
                }

                endSection();
            }

            endSection();
        }

        /**
         * Render the license content into the report.
         *
         * @param licenseUrl the license URL
         */
        private void renderLicenseContent(URL licenseUrl) {
            try {
                // All licenses are supposed in English...
                String licenseContent = ProjectInfoReportUtils.getContent(licenseUrl, settings);

                // TODO: we should check for a text/html mime type instead, and possibly use a html parser to do this a bit more cleanly/reliably.
                String licenseContentLC = licenseContent.toLowerCase(Locale.ENGLISH);
                int bodyStart = licenseContentLC.indexOf("<body");
                int bodyEnd = licenseContentLC.indexOf("</body>");

                if ((licenseContentLC.contains("<!doctype html") || licenseContentLC.contains("<html>"))
                        && ((bodyStart >= 0) && (bodyEnd > bodyStart))) {
                    bodyStart = licenseContentLC.indexOf(">", bodyStart) + 1;
                    String body = licenseContent.substring(bodyStart, bodyEnd);

                    link(licenseUrl.toExternalForm(), "[Original text]");
                    paragraph("Copy of the license follows.");

                    body = replaceRelativeLinks(body, baseURL(licenseUrl).toExternalForm());
                    sink.rawText(body);
                } else {
                    verbatimText(licenseContent);
                }
            } catch (IOException e) {
                paragraph("Can't read the url [" + licenseUrl + "] : " + e.getMessage());
            }
        }

        private static URL baseURL(URL aUrl) {
            String urlTxt = aUrl.toExternalForm();
            int lastSlash = urlTxt.lastIndexOf('/');
            if (lastSlash > -1) {
                try {
                    return new URL(urlTxt.substring(0, lastSlash + 1));
                } catch (MalformedURLException e) {
                    throw new AssertionError(e);
                }
            }

            return aUrl;
        }

        private static String replaceRelativeLinks(String html, String baseURL) {
            String url = baseURL;
            if (!url.endsWith("/")) {
                url += "/";
            }

            String serverURL = url.substring(0, url.indexOf('/', url.indexOf("//") + 2));

            String content = replaceParts(html, url, serverURL, "[aA]", "[hH][rR][eE][fF]");
            content = replaceParts(content, url, serverURL, "[iI][mM][gG]", "[sS][rR][cC]");
            return content;
        }

        private static String replaceParts(String html, String baseURL, String serverURL, String tagPattern,
                String attributePattern) {
            Pattern anchor = Pattern.compile(
                    "(<\\s*" + tagPattern + "\\s+[^>]*" + attributePattern + "\\s*=\\s*\")([^\"]*)\"([^>]*>)");
            StringBuilder sb = new StringBuilder(html);

            int indx = 0;
            boolean done = false;
            while (!done) {
                Matcher mAnchor = anchor.matcher(sb);
                if (mAnchor.find(indx)) {
                    indx = mAnchor.end(3);

                    if (mAnchor.group(2).startsWith("#")) {
                        // relative link - don't want to alter this one!
                    }
                    if (mAnchor.group(2).startsWith("/")) {
                        // root link
                        sb.insert(mAnchor.start(2), serverURL);
                        indx += serverURL.length();
                    } else if (mAnchor.group(2).indexOf(':') < 0) {
                        // relative link
                        sb.insert(mAnchor.start(2), baseURL);
                        indx += baseURL.length();
                    }
                } else {
                    done = true;
                }
            }
            return sb.toString();
        }
    }
}