com.hello2morrow.sonargraph.jenkinsplugin.model.TimeSeriesPlot.java Source code

Java tutorial

Introduction

Here is the source code for com.hello2morrow.sonargraph.jenkinsplugin.model.TimeSeriesPlot.java

Source

/*******************************************************************************
 * Jenkins Sonargraph Plugin
 * Copyright (C) 2009-2015 hello2morrow GmbH
 * mailto: info AT hello2morrow DOT com
 *
 * 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.hello2morrow.sonargraph.jenkinsplugin.model;

import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.MovingAverage;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.TimeSeriesDataItem;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.TextAnchor;

import com.hello2morrow.sonargraph.jenkinsplugin.foundation.SonargraphLogger;
import com.hello2morrow.sonargraph.jenkinsplugin.foundation.StringUtility;

public class TimeSeriesPlot extends AbstractPlot {
    private static final int MOVING_AVG_PERIOD = 1000 * 60 * 60 * 24 * 1;
    private int m_markerPosition = 0;
    private long m_markerTimestamp = 0;

    public TimeSeriesPlot(IMetricHistoryProvider dataProvider, int markerPosition) {
        super(dataProvider);
        m_markerPosition = markerPosition;
    }

    @Override
    protected JFreeChart createChartInternal(String chartTitle, String categoryName, String yAxisName,
            XYDataset dataset) {
        return ChartFactory.createTimeSeriesChart(chartTitle, categoryName, yAxisName, dataset, false, false,
                false);
    }

    /**
     * Creates a XYDataset from a CSV file.
     */
    @Override
    protected XYDataset createXYDataset(SonargraphMetrics metric, int maximumNumberOfDataPoints)
            throws IOException {
        assert metric != null : "Parameter 'metric' of method 'createXYDataset' must not be null";

        //For some reason, the class of the time series is required here, otherwise an exception is thrown that a Date instance is expected.
        @SuppressWarnings("deprecation")
        TimeSeries timeSeries = new TimeSeries(metric.getShortDescription(), FixedMillisecond.class);

        List<IDataPoint> dataset = m_datasetProvider.readMetricValues(metric);
        int size = dataset.size();
        SonargraphLogger.INSTANCE.fine(size + " data points found for metric '" + metric.getStandardName()
                + "' in file '" + m_datasetProvider.getStorageName() + "'");
        List<IDataPoint> reducedSet = reduceDataSet(dataset, maximumNumberOfDataPoints);

        BuildDataPoint point = null;
        for (IDataPoint datapoint : reducedSet) {
            if (datapoint instanceof InvalidDataPoint) {
                // We could create a gap in the graph by adding null:
                // xySeries.add(datapoint.getX(), null);
                continue;
            } else if (datapoint instanceof BuildDataPoint) {
                point = (BuildDataPoint) datapoint;
                if (point.getTimestamp() == 0) {
                    continue;
                }

                timeSeries.add(new FixedMillisecond(point.getTimestamp()), point.getY());
            }
        }
        if (point != null) {
            setTimestampOfLastDisplayedPoint(point.getTimestamp());
        }

        TimeSeriesCollection timeSeriesCollection = new TimeSeriesCollection();
        TimeSeries avgDataset = MovingAverage.createMovingAverage(timeSeries,
                "Avg of " + metric.getShortDescription(), MOVING_AVG_PERIOD, 0);
        setDataSetSize(avgDataset.getItemCount());
        timeSeriesCollection.addSeries(avgDataset);

        //SG-325: We cannot use JFreeChart methods of version 1.0.14
        //        setMinimumValue(avgDataset.getMinY());
        //        setMaximumValue(avgDataset.getMaxY());

        // We only show the average data and omit the original data
        //        timeSeriesCollection.addSeries(timeSeries);
        for (Object item : avgDataset.getItems()) {
            if (item instanceof TimeSeriesDataItem) {
                checkMinMaxYValue(((TimeSeriesDataItem) item).getValue().doubleValue());
            }
        }
        return timeSeriesCollection;
    }

    /**
     * 
     * @param maxDataPoints don't reduce the set if  maxDataPoints <= 0
     */
    private List<IDataPoint> reduceDataSet(List<IDataPoint> dataset, int maxDataPoints) {
        assert dataset != null : "Parameter 'dataset' of method 'reduceDataSet' must not be null";
        if (maxDataPoints <= 0) {
            return dataset;
        }
        int size = dataset.size();
        if (size <= maxDataPoints) {
            if (size > m_markerPosition) {
                IDataPoint point = dataset.get(dataset.size() - m_markerPosition);
                if (point instanceof BuildDataPoint) {
                    m_markerTimestamp = ((BuildDataPoint) point).getTimestamp();
                }
            }
            return dataset;
        }

        int compressionFactor = 2;

        if ((size % maxDataPoints) == 0) {
            compressionFactor = size / maxDataPoints;
        } else {
            compressionFactor = (size / maxDataPoints) + 1;
        }

        List<IDataPoint> compressedSet = new ArrayList<IDataPoint>();
        SonargraphLogger.INSTANCE.log(Level.FINE,
                "Compressing data set of size '" + size + "' by a factor of '" + compressionFactor + "'");
        for (int i = 0; i < size; i = i + compressionFactor) {
            double valueSum = 0.0;
            long timestamp = 0L;
            int buildNumber = 0;
            int actualFactor = 0;
            for (int j = 0; j < compressionFactor; j++) {
                if ((i + j) >= size) {
                    break;
                }
                actualFactor = j + 1;
                IDataPoint point = dataset.get(i + j);
                if (point instanceof BuildDataPoint) {
                    valueSum += point.getY();
                    buildNumber = point.getX();
                    timestamp = ((BuildDataPoint) point).getTimestamp();
                    if ((i + j) == (size - m_markerPosition)) {
                        m_markerTimestamp = timestamp;
                    }
                } else {
                    SonargraphLogger.INSTANCE.log(Level.FINE,
                            "DataPoint [" + (i + j) + "] is of type '" + point.getClass().getName()
                                    + "', expect type '" + BuildDataPoint.class.getName() + "'");
                }
            }
            compressedSet.add(new BuildDataPoint(buildNumber, valueSum / actualFactor, timestamp));
        }
        return compressedSet;
    }

    @Override
    protected void applyRendering(XYPlot plot) {
        DateAxis axis = (DateAxis) plot.getDomainAxis();
        axis.setDateFormatOverride(StringUtility.getDateFormat());

        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
        renderer.setBaseShapesVisible(false);
        renderer.setSeriesPaint(0, DATA_COLOR);

        if (m_markerTimestamp > 0) {
            final Marker target = new ValueMarker(m_markerTimestamp);
            target.setPaint(Color.RED);
            target.setLabel("Short Term");
            if ((m_markerPosition * 2) > getDatasetSize()) {
                //Move the label to the left of the marker
                target.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
                target.setLabelTextAnchor(TextAnchor.TOP_LEFT);
            } else {
                target.setLabelAnchor(RectangleAnchor.TOP_LEFT);
                target.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
            }
            plot.addDomainMarker(target);
        }
    }
}