hudson.plugins.jacoco.portlet.chart.JacocoBuilderTrendChart.java Source code

Java tutorial

Introduction

Here is the source code for hudson.plugins.jacoco.portlet.chart.JacocoBuilderTrendChart.java

Source

/*
 *  The MIT License
 *
 *  Copyright 2010 Sony Ericsson Mobile Communications. All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

/**
 * @author Allyn Pierre (Allyn.GreyDeAlmeidaLimaPierre@sonyericsson.com)
 * @author Eduardo Palazzo (Eduardo.Palazzo@sonyericsson.com)
 * @author Mauro Durante (Mauro.DuranteJunior@sonyericsson.com)
 */
package hudson.plugins.jacoco.portlet.chart;

import hudson.plugins.jacoco.portlet.Messages;
import hudson.plugins.jacoco.portlet.JacocoLoadData;
import hudson.plugins.jacoco.portlet.bean.JacocoCoverageResultSummary;
import hudson.plugins.jacoco.portlet.utils.Constants;
import hudson.plugins.jacoco.portlet.utils.Utils;

import java.awt.BasicStroke;
import java.awt.Color;
import java.util.List;
import java.util.Map;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.data.category.CategoryDataset;
import org.joda.time.LocalDate;

import hudson.Extension;
import hudson.model.Descriptor;
import hudson.model.Job;
import hudson.plugins.view.dashboard.DashboardPortlet;
import hudson.util.DataSetBuilder;
import hudson.util.Graph;
import hudson.util.ShiftedCategoryAxis;
import org.kohsuke.stapler.DataBoundConstructor;

/**
 * A portlet for JaCoCo coverage results - Trend Chart.
 */
public class JacocoBuilderTrendChart extends DashboardPortlet {

    /**
     * Chart width that can be set by user.
     */
    private final int width;

    /**
     * Chart height that can be set by user.
     */
    private final int height;

    /**
     * Number of days of the chart that can be set by user.
     */
    private final int daysNumber;

    /**
     * Constructor with chart attributes as parameters.
     * DataBoundConstructor annotation helps the Stapler class to find
     * which constructor that should be used when automatically copying
     * values from a web form to a class.
     *
     * @param name
     *          chart name
     * @param width
     *          the chart width
     * @param height
     *          the chart height
     * @param daysNumber
     *          the number of days
     */
    @DataBoundConstructor
    public JacocoBuilderTrendChart(String name, String width, String height, String daysNumber) {

        super(name);

        this.width = Utils.validateChartAttributes(width, Constants.DEFAULT_WIDTH);
        this.height = Utils.validateChartAttributes(height, Constants.DEFAULT_HEIGHT);
        this.daysNumber = Utils.validateChartAttributes(daysNumber, Constants.DEFAULT_DAYS_NUMBER);
    }

    /**
     * This method will be called by portlet.jelly to load data and
     * create the chart.
     *
     * @return Graph a summary graph
     */
    public Graph getSummaryGraph() {

        Map<LocalDate, JacocoCoverageResultSummary> summaries;

        // Retrieve Dashboard View jobs
        List<Job> jobs = getDashboard().getJobs();

        // Fill a HashMap with the data will be showed in the chart
        summaries = JacocoLoadData.loadChartDataWithinRange(jobs, daysNumber);

        return createTrendChart(summaries, width, height);
    }

    /**
     * Creates a graph for JaCoCo Coverage results.
     *
     * @param summaries
     *          HashMap(key = run date and value = Instrumentation tests
     *          results)
     * @param widthParam
     *          the chart width
     * @param heightParam
     *          the chart height
     * @return Graph (JFreeChart)
     */
    private static Graph createTrendChart(final Map<LocalDate, JacocoCoverageResultSummary> summaries,
            int widthParam, int heightParam) {

        return new Graph(-1, widthParam, heightParam) {

            @Override
            protected JFreeChart createGraph() {

                // Show empty chart
                if (summaries == null) {
                    JFreeChart chart = ChartFactory.createStackedAreaChart(null, Constants.AXIS_LABEL,
                            Constants.AXIS_LABEL_VALUE, null, PlotOrientation.VERTICAL, true, false, false);

                    return chart;
                }

                int lineNumber = 0;

                JFreeChart chart = ChartFactory.createLineChart("", Constants.AXIS_LABEL,
                        Constants.AXIS_LABEL_VALUE, buildDataSet(summaries), PlotOrientation.VERTICAL, true, false,
                        false);

                chart.setBackgroundPaint(Color.white);

                CategoryPlot plot = chart.getCategoryPlot();

                // Line thickness
                CategoryItemRenderer renderer = plot.getRenderer();
                BasicStroke stroke = new BasicStroke(Constants.LINE_THICKNESS, BasicStroke.CAP_ROUND,
                        BasicStroke.JOIN_ROUND);
                renderer.setSeriesStroke(lineNumber++, stroke);
                renderer.setSeriesStroke(lineNumber++, stroke);
                renderer.setSeriesStroke(lineNumber++, stroke);
                renderer.setSeriesStroke(lineNumber, stroke);

                plot.setBackgroundPaint(Color.WHITE);
                plot.setOutlinePaint(null);
                plot.setForegroundAlpha(Constants.FOREGROUND_ALPHA);
                plot.setRangeGridlinesVisible(true);
                plot.setRangeGridlinePaint(Color.black);

                CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
                plot.setDomainAxis(domainAxis);
                domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
                domainAxis.setLowerMargin(Constants.DEFAULT_MARGIN);
                domainAxis.setUpperMargin(Constants.DEFAULT_MARGIN);
                domainAxis.setCategoryMargin(Constants.DEFAULT_MARGIN);

                NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
                rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
                //rangeAxis.setUpperBound(Constants.UPPER_BOUND);
                rangeAxis.setLowerBound(Constants.LOWER_BOUND);

                return chart;

            }
        };
    }

    /**
     * Build data set.
     *
     * @param summaries
     *          HashMap containing data of chart.
     * @return CategoryDataset Interface for a dataset with one or more
     *         series, and values associated with categories.
     */
    private static CategoryDataset buildDataSet(Map<LocalDate, JacocoCoverageResultSummary> summaries) {

        DataSetBuilder<String, LocalDate> dataSetBuilder = new DataSetBuilder<String, LocalDate>();

        for (Map.Entry<LocalDate, JacocoCoverageResultSummary> entry : summaries.entrySet()) {
            float classCoverage = 0;
            float lineCoverage = 0;
            float methodCoverage = 0;
            float branchCoverage = 0;
            float instructionCoverage = 0;
            float complexityScore = 0;

            int count = 0;

            List<JacocoCoverageResultSummary> list = entry.getValue().getJacocoCoverageResults();

            for (JacocoCoverageResultSummary item : list) {
                classCoverage += item.getClassCoverage();
                lineCoverage += item.getLineCoverage();
                methodCoverage += item.getMethodCoverage();
                branchCoverage += item.getBranchCoverage();
                instructionCoverage += item.getInstructionCoverage();
                complexityScore += item.getComplexityScore();
                count++;
            }

            dataSetBuilder.add((classCoverage / count), "class", entry.getKey());
            dataSetBuilder.add((lineCoverage / count), "line", entry.getKey());
            dataSetBuilder.add((methodCoverage / count), "method", entry.getKey());
            dataSetBuilder.add((branchCoverage / count), "branch", entry.getKey());
            dataSetBuilder.add((instructionCoverage / count), "instruction", entry.getKey());

            // XXX this should be a separate chart. the range axis is different.
            dataSetBuilder.add((complexityScore / count), "complexity", entry.getKey());
        }

        return dataSetBuilder.build();
    }

    /**
     * Descriptor that will be shown on Dashboard Portlets view.
     */
    @Extension(optional = true)
    public static class DescriptorImpl extends Descriptor<DashboardPortlet> {

        @Override
        public String getDisplayName() {
            return Messages.ChartTitle();
        }
    }

    /**
     * Getter of the width.
     *
     * @return int the width
     */
    public int getWidth() {
        return width;
    }

    /**
     * Getter of the height.
     *
     * @return int the height
     */
    public int getHeight() {
        return height;
    }

    /**
     * Getter of the number of days.
     *
     * @return int the number of days
     */
    public int getDaysNumber() {
        return daysNumber;
    }
}