001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.server.charts.jruby;
021    
022    import org.apache.commons.lang.LocaleUtils;
023    import org.jfree.chart.JFreeChart;
024    import org.jfree.chart.axis.AxisLocation;
025    import org.jfree.chart.axis.DateAxis;
026    import org.jfree.chart.axis.NumberAxis;
027    import org.jfree.chart.plot.Marker;
028    import org.jfree.chart.plot.ValueMarker;
029    import org.jfree.chart.plot.XYPlot;
030    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
031    import org.jfree.chart.title.TextTitle;
032    import org.jfree.data.time.Day;
033    import org.jfree.data.time.TimeSeries;
034    import org.jfree.data.time.TimeSeriesCollection;
035    import org.jfree.ui.RectangleAnchor;
036    import org.jfree.ui.RectangleEdge;
037    import org.jfree.ui.RectangleInsets;
038    import org.jfree.ui.TextAnchor;
039    import org.sonar.server.charts.deprecated.BaseChart;
040    
041    import java.awt.*;
042    import java.awt.image.BufferedImage;
043    import java.io.IOException;
044    import java.text.DateFormat;
045    import java.text.DecimalFormat;
046    import java.text.ParseException;
047    import java.util.Date;
048    import java.util.SortedMap;
049    import java.util.TreeMap;
050    
051    public class TrendsChart extends BaseChart {
052    
053      private XYPlot plot;
054      private SortedMap<Long, TimeSeries> seriesById;
055      private int percentAxisId = -1;
056      private boolean displayLegend;
057    
058      public TrendsChart(int width, int height, String localeKey, boolean displayLegend) {
059        super(width, height);
060        this.displayLegend = displayLegend;
061        seriesById = new TreeMap<Long, TimeSeries>();
062        plot = new XYPlot();
063        DateAxis dateAxis = new DateAxis();
064        dateAxis.setDateFormatOverride(DateFormat.getDateInstance(DateFormat.SHORT, LocaleUtils.toLocale(localeKey)));
065        plot.setDomainAxis(dateAxis);
066      }
067    
068      public void initSerie(Long serieId, String legend, boolean isPercent) {
069        TimeSeries series = new TimeSeries(legend);
070    
071        int index=seriesById.size();
072        seriesById.put(serieId, series);
073    
074        TimeSeriesCollection timeSeriesColl = new TimeSeriesCollection();
075        timeSeriesColl.addSeries(series);
076        plot.setDataset(index, timeSeriesColl);
077    
078        if (isPercent) {
079          if (percentAxisId == -1) {
080            NumberAxis rangeAxis = new NumberAxis();
081            rangeAxis.setNumberFormatOverride(new DecimalFormat("0'%'"));
082            rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
083            rangeAxis.setUpperBound(100.0);
084            rangeAxis.setLowerBound(0.0);
085            plot.setRangeAxisLocation(index, AxisLocation.TOP_OR_LEFT);
086            plot.setRangeAxis(index, rangeAxis);
087            plot.mapDatasetToRangeAxis(index, index);
088            percentAxisId = index;
089    
090          } else {
091            plot.mapDatasetToRangeAxis(index, percentAxisId);
092          }
093        } else {
094          NumberAxis rangeAxis = new NumberAxis(displayLegend ? legend : null);
095          rangeAxis.setAutoRangeIncludesZero(false);
096          rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
097          rangeAxis.setAutoRangeMinimumSize(2.0);
098          plot.setRangeAxisLocation(index, AxisLocation.TOP_OR_RIGHT);
099          plot.setRangeAxis(index, rangeAxis);
100          plot.mapDatasetToRangeAxis(index, index);
101        }
102    
103        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
104        renderer.setBaseShapesVisible(false);
105        renderer.setSeriesStroke(0, new BasicStroke(2.0f));
106        renderer.setSeriesPaint(0, COLORS[index % COLORS.length]);
107        plot.setRenderer(index, renderer);
108      }
109    
110      public void addMeasure(Double value, Date date, Long serieId) {
111        seriesById.get(serieId).addOrUpdate(new Day(date), value);
112      }
113    
114      public void addLabel(Date date, String label) throws ParseException {
115        addLabel(date, label, false);
116      }
117    
118      public void addLabel(Date date, String label, boolean lower) throws ParseException {
119        Day d = new Day(date);
120        double millis = d.getFirstMillisecond();
121        Marker marker = new ValueMarker(millis);
122        marker.setLabel(label);
123        marker.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
124        marker.setLabelTextAnchor(TextAnchor.TOP_LEFT);
125        Color c = new Color(17, 40, 95);
126        marker.setLabelPaint(c);
127        marker.setPaint(c);
128        marker.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3.0f, new float[]{5f, 5f, 5f, 5f}, 2.0f));
129        if (lower) {
130          marker.setLabelOffset(new RectangleInsets(18, 0, 0, 5));
131        }
132        plot.addDomainMarker(marker);
133      }
134    
135      @Override
136      protected BufferedImage getChartImage() throws IOException {
137        JFreeChart chart = new JFreeChart(null, TextTitle.DEFAULT_FONT, plot, true);
138        configureChart(chart, displayLegend ? RectangleEdge.BOTTOM : null);
139        return super.getBufferedImage(chart);
140      }
141    }