com.redhat.rcm.maven.plugin.buildmetadata.BuildReportRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.rcm.maven.plugin.buildmetadata.BuildReportRenderer.java

Source

/*
 * Copyright 2006-2014 smartics, Kronseder & Reiner GmbH
 *
 * Licensed 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 com.redhat.rcm.maven.plugin.buildmetadata;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.lang.ObjectUtils;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;

import com.redhat.rcm.maven.plugin.buildmetadata.common.Constant;
import com.redhat.rcm.maven.plugin.buildmetadata.common.Property;
import com.redhat.rcm.maven.plugin.buildmetadata.common.Constant.Section;
import com.redhat.rcm.maven.plugin.buildmetadata.util.FilePathNormalizer;
import com.redhat.rcm.maven.plugin.buildmetadata.util.NoopNormalizer;
import com.redhat.rcm.maven.plugin.buildmetadata.util.Normalizer;

/**
 * Renders the build report.
 *
 * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
 * @version $Revision:591 $
 */
public final class BuildReportRenderer { // NOPMD
                                         // ********************************* Fields *********************************

    // --- constants ------------------------------------------------------------

    // --- members --------------------------------------------------------------

    /**
     * The normalizer to be applied to file name value to remove the base dir
     * prefix.
     */
    private final FilePathNormalizer filePathNormalizer;

    /**
     * The sink to write to.
     */
    private final Sink sink;

    /**
     * The resource bundle to access localized messages.
     */
    private final ResourceBundle messages;

    /**
     * The properties file to read the build information from.
     */
    private final File buildMetaDataPropertiesFile;

    /**
     * The list of a system properties or environment variables to be selected by
     * the user to include into the build meta data properties.
     * <p>
     * The name is the name of the property, the section is relevant for placing
     * the property in one of the following sections:
     * </p>
     * <ul>
     * <li><code>build.scm</code></li>
     * <li><code>build.dateAndVersion</code></li>
     * <li><code>build.runtime</code></li>
     * <li><code>build.java</code></li>
     * <li><code>build.maven</code></li>
     * <li><code>build.misc</code></li>
     * </ul>
     * <p>
     * If no valid section is given, the property is silently rendered in the
     * <code>build.misc</code> section.
     * </p>
     *
     * @parameter
     */
    private final List<Property> properties;

    // ****************************** Initializer *******************************

    // ****************************** Constructors ******************************

    /**
     * Default constructor.
     *
     * @param filePathNormalizer the normalizer to be applied to file name value
     *          to remove the base dir prefix.
     * @param messages the resource bundle to access localized messages.
     * @param sink the sink to write to.
     * @param buildMetaDataPropertiesFile the properties file to read the build
     *          information from.
     * @param properties the list of a system properties or environment variables
     *          to be selected by the user to include into the build meta data
     *          properties.
     */
    public BuildReportRenderer(final FilePathNormalizer filePathNormalizer, final ResourceBundle messages,
            final Sink sink, final File buildMetaDataPropertiesFile, final List<Property> properties) {
        this.filePathNormalizer = filePathNormalizer;
        this.sink = sink;
        this.messages = messages;
        this.buildMetaDataPropertiesFile = buildMetaDataPropertiesFile;
        this.properties = properties;
    }

    // ****************************** Inner Classes *****************************

    // ********************************* Methods ********************************

    // --- init -----------------------------------------------------------------

    // --- get&set --------------------------------------------------------------

    // --- business -------------------------------------------------------------

    /**
     * Renders the report to the instance's sink.
     *
     * @throws MavenReportException if the report cannot be rendered.
     */
    public void renderReport() throws MavenReportException {
        sink.head();
        sink.title();
        sink.text(messages.getString("report.name"));
        sink.title_();
        sink.head_();

        sink.body();
        renderBody();
        sink.body_();
        sink.flush();
        sink.close();
    }

    /**
     * Renders the body of the report.
     *
     * @throws MavenReportException if the report cannot be rendered.
     */
    private void renderBody() throws MavenReportException {
        sink.section1();

        sink.sectionTitle1();
        sink.text(messages.getString("report.name"));
        sink.sectionTitle1_();

        sink.paragraph();
        sink.text(messages.getString("report.description"));
        sink.paragraph_();

        final Properties buildMetaDataProperties = readBuildMetaDataProperties();

        renderSections(buildMetaDataProperties);

        renderFooter();
        sink.section1_();
    }

    private void renderSections(final Properties buildMetaDataProperties) {
        for (final Section section : Constant.REPORT_PROPERTIES) {
            final List<String> properties = section.getProperties();
            if (hasPropertiesProvided(buildMetaDataProperties, properties)) {
                final String sectionKey = section.getTitleKey();
                sink.sectionTitle2();
                sink.text(messages.getString(sectionKey));
                sink.sectionTitle2_();
                renderTableStart();
                for (final String key : properties) {
                    renderCell(buildMetaDataProperties, key);
                }
                renderSelectedPropertiesForSection(buildMetaDataProperties, sectionKey);
                renderTableEnd();
            }
        }

        renderNonStandardProperties(buildMetaDataProperties);
    }

    private boolean hasPropertiesProvided(final Properties buildMetaDataProperties, final List<String> properties) {
        for (final String key : properties) {
            final Object value = buildMetaDataProperties.get(key);
            if (value != null && StringUtils.isNotBlank(String.valueOf(value))) {
                return true;
            }
        }

        final Set<String> selectedProperties = createSelectedProperties();
        for (final String key : selectedProperties) {
            final Object value = buildMetaDataProperties.get(key);
            if (value != null && StringUtils.isNotBlank(String.valueOf(value))) {
                return true;
            }
        }

        return false;
    }

    private void renderSelectedPropertiesForSection(final Properties buildMetaDataProperties,
            final String sectionKey) {
        if (properties != null && !properties.isEmpty()) {
            for (final Property property : properties) {
                if (sectionKey.equals(property.getSection())) {
                    final String key = property.getName();
                    renderCell(buildMetaDataProperties, key);
                }
            }
        }
    }

    private void renderNonStandardProperties(final Properties buildMetaDataProperties) {
        final Properties nonStandardProperties = Constant.calcNonStandardProperties(buildMetaDataProperties,
                properties);
        if (!nonStandardProperties.isEmpty()) {
            sink.sectionTitle2();
            sink.text(messages.getString(Constant.SECTION_BUILD_MISC));
            sink.sectionTitle2_();
            renderTableStart();
            for (final Enumeration<Object> en = nonStandardProperties.keys(); en.hasMoreElements();) {
                final String key = String.valueOf(en.nextElement());
                if (Constant.isIntendedForMiscSection(key)) {
                    renderCell(nonStandardProperties, key);
                }
            }
            renderTableEnd();
        }
    }

    private Set<String> createSelectedProperties() {
        final Set<String> selectedProperties = new HashSet<String>();

        if (properties != null) {
            for (final Property property : properties) {
                selectedProperties.add(property.getName());
            }
        }

        return selectedProperties;
    }

    private void renderTableEnd() {
        sink.table_();
    }

    private void renderTableStart() {
        sink.table();
        sink.tableRow();
        sink.tableHeaderCell("200");
        final String topicLabel = messages.getString("report.table.header.topic");
        sink.text(topicLabel);
        sink.tableHeaderCell_();
        sink.tableHeaderCell();
        final String valueLabel = messages.getString("report.table.header.value");
        sink.text(valueLabel);
        sink.tableHeaderCell_();
        sink.tableRow_();
    }

    /**
     * Renders a single cell of the table.
     *
     * @param buildMetaDataProperties build meta data properties to access the
     *          data to be rendered.
     * @param key the key to the data to be rendered.
     */
    private void renderCell(final Properties buildMetaDataProperties, final String key) {
        final Object value = buildMetaDataProperties.get(key);
        if (value != null) {
            sink.tableRow();
            sink.tableCell();
            sink.text(getLabel(key));
            sink.tableCell_();
            sink.tableCell();
            if (Constant.PROP_NAME_MAVEN_ACTIVE_PROFILES.equals(key)) {
                renderMultiTupleValue(buildMetaDataProperties, value, Constant.MAVEN_ACTIVE_PROFILE_PREFIX);
            } else if (Constant.PROP_NAME_SCM_LOCALLY_MODIFIED_FILES.equals(key)) {
                final String filesValue = Constant.prettifyFilesValue(value);
                renderMultiValue(filesValue, NoopNormalizer.INSTANCE);
            } else if (Constant.PROP_NAME_MAVEN_GOALS.equals(key)) {
                renderMultiValue(value, NoopNormalizer.INSTANCE);
            } else if (Constant.PROP_NAME_MAVEN_FILTERS.equals(key)) {
                renderMultiValue(value, filePathNormalizer);
            } else {
                renderSingleValue(value);
            }
            sink.tableCell_();
            sink.tableRow_();
        }
    }

    private void renderSingleValue(final Object value) {
        final String stringValue = String.valueOf(value);
        if (stringValue != null && !isLink(stringValue)) {
            sink.text(stringValue);
        } else {
            sink.link(stringValue);
            sink.text(stringValue);
            sink.link_();
        }
    }

    private boolean isLink(final String input) {
        return (input.startsWith("http://") || input.startsWith("https://"));
    }

    private void renderMultiTupleValue(final Properties buildMetaDataProperties, final Object value,
            final String subKeyPrefix) {
        final String stringValue = Constant.prettify((String) value);
        if (hasMultipleValues(stringValue)) {
            final StringTokenizer tokenizer = new StringTokenizer(stringValue, ",");
            sink.numberedList(Sink.NUMBERING_DECIMAL);
            while (tokenizer.hasMoreTokens()) {
                final String profileName = tokenizer.nextToken().trim();
                final String subKey = subKeyPrefix + '.' + profileName;
                final Object subValue = buildMetaDataProperties.get(subKey);
                final String item = profileName + ':' + subValue;
                sink.listItem();
                sink.text(item);
                sink.listItem_();
            }
            sink.numberedList_();
        } else {
            sink.text(String.valueOf(value));
        }
    }

    private void renderMultiValue(final Object value, final Normalizer normalizer) {
        final String stringValue = Constant.prettify(ObjectUtils.toString(value));
        if (hasMultipleValues(stringValue)) {
            final StringTokenizer tokenizer = new StringTokenizer(stringValue, ",");
            sink.numberedList(Sink.NUMBERING_DECIMAL);
            while (tokenizer.hasMoreTokens()) {
                final String subValue = tokenizer.nextToken().trim();
                final String textValue = normalizer.normalize(subValue);
                sink.listItem();
                sink.text(textValue);
                sink.listItem_();
            }
            sink.numberedList_();
        } else {
            final String textValue = normalizer.normalize(stringValue);
            sink.text(textValue);
        }
    }

    private boolean hasMultipleValues(final String stringValue) {
        return stringValue.indexOf(',') != -1;
    }

    private String getLabel(final String key) {
        try {
            return messages.getString(key);
        } catch (final MissingResourceException e) {
            if (properties != null) {
                for (final Property property : properties) {
                    final String label = property.getLabel();
                    if (StringUtils.isNotBlank(label) && key.equals(property.getMappedName())) {
                        return label;
                    }
                }
            }
            return key;
        }
    }

    /**
     * Renders the footer text.
     */
    private void renderFooter() {
        final String footerText = messages.getString("report.footer");
        if (StringUtils.isNotBlank(footerText)) {
            sink.rawText(footerText);
        }
    }

    /**
     * Reads the build meta data properties from the well known location.
     *
     * @return the read properties.
     * @throws MavenReportException if the properties cannot be read.
     */
    private Properties readBuildMetaDataProperties() throws MavenReportException {
        final Properties buildMetaDataProperties = new Properties();
        InputStream inStream = null;
        try {
            inStream = new BufferedInputStream(new FileInputStream(this.buildMetaDataPropertiesFile));
            buildMetaDataProperties.load(inStream);
        } catch (final IOException e) {
            throw new MavenReportException(
                    "Cannot read build properties file '" + this.buildMetaDataPropertiesFile + "'.", e);
        } finally {
            IOUtil.close(inStream);
        }
        return buildMetaDataProperties;
    }

    // --- object basics --------------------------------------------------------

}