/*
* Copyright 2006 Pentaho Corporation. All rights reserved.
* This software was developed by Pentaho Corporation and is provided under the terms
* of the Mozilla Public License, Version 1.1, or any later version. You may not use
* this file except in compliance with the license. If you need a copy of the license,
* please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
* BI Platform. The Initial Developer is Pentaho Corporation.
*
* Software distributed under the Mozilla Public License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
* the license for the specific language governing your rights and limitations.
*
* @author Ingo Klose
*
*/
package org.pentaho.plugin.jfreereport.reportcharts;
import java.awt.Font;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberAxis3D;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLine3DRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.general.Dataset;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.Minute;
import org.jfree.data.time.Month;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.pentaho.plugin.jfreereport.reportcharts.backport.ExtTimeTableXYDataset;
import org.pentaho.plugin.jfreereport.reportcharts.backport.FastNumberTickUnit;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.libraries.formatting.FastDecimalFormat;
public class XYAreaLineChartExpression extends XYAreaChartExpression implements MultiPlotChartExpression
{
private static final long serialVersionUID = 7082583397390897215L;
private String secondValueAxisLabel; //$NON-NLS-1$
private ArrayList lineSeriesColor;
private Font linesLabelFont; //$NON-NLS-1$
private Font linesTickLabelFont; //$NON-NLS-1$
private String lineTicksLabelFormat; //$NON-NLS-1$
private boolean sharedRangeAxis;
private double linePeriodCount;
private Class lineTimePeriod;
private Font lineTitleFont;
private Font lineTickFont;
private Double lineRangeMinimum;
private Double lineRangeMaximum;
private String secondaryDataSet;
//constructor
public XYAreaLineChartExpression()
{
lineSeriesColor = new ArrayList();
secondValueAxisLabel = "";
linePeriodCount = 0;
}
public Font getLineTitleFont()
{
return lineTitleFont;
}
public void setLineTitleFont(final Font lineTitleFont)
{
this.lineTitleFont = lineTitleFont;
}
public Font getLineTickFont()
{
return lineTickFont;
}
public void setLineTickFont(final Font lineTickFont)
{
this.lineTickFont = lineTickFont;
}
public Double getLineRangeMinimum()
{
return lineRangeMinimum;
}
public void setLineRangeMinimum(final Double lineRangeMinimum)
{
this.lineRangeMinimum = lineRangeMinimum;
}
public Double getLineRangeMaximum()
{
return lineRangeMaximum;
}
public void setLineRangeMaximum(final Double lineRangeMaximum)
{
this.lineRangeMaximum = lineRangeMaximum;
}
public double getLinePeriodCount()
{
return linePeriodCount;
}
public void setLinePeriodCount(final double linePeriodCount)
{
this.linePeriodCount = linePeriodCount;
}
public Class getLineTimePeriod()
{
return lineTimePeriod;
}
public void setLineTimePeriod(final Class lineTimePeriod)
{
this.lineTimePeriod = lineTimePeriod;
}
public boolean isSharedRangeAxis()
{
return sharedRangeAxis;
}
public void setSharedRangeAxis(final boolean sharedRangeAxis)
{
this.sharedRangeAxis = sharedRangeAxis;
}
/**
* Return a completly separated copy of this function. The copy does no longer share any changeable objects with the
* original function.
*
* @return a copy of this function.
*/
public Expression getInstance()
{
final XYAreaLineChartExpression chartExpression = (XYAreaLineChartExpression) super.getInstance();
chartExpression.lineSeriesColor = (ArrayList) lineSeriesColor.clone();
return chartExpression;
}
public String getSecondaryDataSet()
{
return secondaryDataSet;
}
public void setSecondaryDataSet(final String dataset)
{
secondaryDataSet = dataset;
}
public String getSecondValueAxisLabel()
{
return secondValueAxisLabel;
}
public void setSecondValueAxisLabel(final String secondValueAxisLabel)
{
this.secondValueAxisLabel = secondValueAxisLabel;
}
public Font getLinesLabelFont()
{
return linesLabelFont;
}
public void setLinesLabelFont(final Font linesLabelFont)
{
this.linesLabelFont = linesLabelFont;
}
public Font getLinesTickLabelFont()
{
return linesTickLabelFont;
}
public void setLinesTickLabelFont(final Font linesTickLabelFont)
{
this.linesTickLabelFont = linesTickLabelFont;
}
public String getLineTicksLabelFormat()
{
return lineTicksLabelFormat;
}
public void setLineTicksLabelFormat(final String lineTicksLabelFormat)
{
this.lineTicksLabelFormat = lineTicksLabelFormat;
}
public void setLineSeriesColor(final int index, final String field)
{
if (lineSeriesColor.size() == index)
{
lineSeriesColor.add(field);
}
else
{
lineSeriesColor.set(index, field);
}
}
public String getLineSeriesColor(final int index)
{
return (String) this.lineSeriesColor.get(index);
}
public int getLineSeriesColorCount()
{
return this.lineSeriesColor.size();
}
public String[] getLineSeriesColor()
{
final Object[] toArray = this.lineSeriesColor.toArray(new String[this.lineSeriesColor.size()]);
return (String[]) toArray;
}
public void setLineSeriesColor(final String[] fields)
{
this.lineSeriesColor.clear();
this.lineSeriesColor.addAll(Arrays.asList(fields));
}
private XYDataset createLinesDataset()
{
final Object maybeCollector = getDataRow().get(getSecondaryDataSet());
final Dataset dataset;
if (maybeCollector instanceof ICollectorFunction)
{
final ICollectorFunction collector = (ICollectorFunction) maybeCollector;
dataset = (Dataset) collector.getDatasourceValue();
}
else if (maybeCollector instanceof CollectorFunctionResult)
{
final CollectorFunctionResult collector = (CollectorFunctionResult) maybeCollector;
dataset = collector.getDataSet();
}
else
{
dataset = null;
}
final XYDataset linesDataset;
if (dataset instanceof XYDataset)
{
linesDataset = (XYDataset) dataset;
}
else
{
linesDataset = null;
}
return linesDataset;
}
protected JFreeChart computeXYChart(final XYDataset xyDataset)
{
final JFreeChart chart;
if (xyDataset instanceof TimeSeriesCollection)
{
if (isStacked())
{
final ExtTimeTableXYDataset tableXYDataset = convertToTable(xyDataset);
chart = createTimeSeriesChart(computeTitle(), getDomainTitle(), getRangeTitle(), tableXYDataset,
isShowLegend(), false, false, isStacked());
}
else
{
chart = createTimeSeriesChart(computeTitle(), getDomainTitle(), getRangeTitle(), xyDataset,
isShowLegend(), false, false, isStacked());
}
}
else
{
final PlotOrientation orientation = computePlotOrientation();
if (isStacked())
{
chart = createStackedXYAreaChart(computeTitle(), getDomainTitle(), getRangeTitle(),
xyDataset, orientation, isShowLegend(), false, false);
}
else
{
chart = ChartFactory.createXYAreaChart(computeTitle(), getDomainTitle(), getRangeTitle(),
xyDataset, orientation, isShowLegend(), false, false);
}
}
configureLogarithmicAxis(chart.getXYPlot());
configureLineChart(chart.getXYPlot());
return chart;
}
protected void configureLineChart(final XYPlot plot)
{
final XYDataset linesDataset = createLinesDataset();
if (linesDataset == null || linesDataset.getSeriesCount() == 0)
{
return;
}
//Create Axis Objects
final ValueAxis linesAxis;
if (isSharedRangeAxis())
{
linesAxis = plot.getRangeAxis();
}
else if (isThreeD())
{
linesAxis = new NumberAxis3D(getSecondValueAxisLabel());
}
else
{
linesAxis = new NumberAxis(getSecondValueAxisLabel());
}
final XYItemRenderer lineRenderer;
if (isThreeD())
{
lineRenderer = new XYLine3DRenderer();
}
else
{
lineRenderer = new XYLineAndShapeRenderer();
}
plot.setRenderer(1, lineRenderer);
plot.setDataset(1, linesDataset);
plot.setRangeAxis(1, linesAxis);
//map lines to second axis
plot.mapDatasetToRangeAxis(1, 1);
//set location of second axis
plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
}
protected void configureChart(final JFreeChart chart)
{
super.configureChart(chart);
final XYPlot plot = chart.getXYPlot();
if (isSharedRangeAxis() == false)
{
final ValueAxis linesAxis = plot.getRangeAxis(1);
if (linesAxis instanceof NumberAxis)
{
final NumberAxis numberAxis = (NumberAxis) linesAxis;
if (getLinePeriodCount() > 0)
{
if (getLineTicksLabelFormat() != null)
{
final FastDecimalFormat formatter = new FastDecimalFormat
(getLineTicksLabelFormat(), getResourceBundleFactory().getLocale());
numberAxis.setTickUnit(new FastNumberTickUnit(getLinePeriodCount(), formatter));
}
else
{
numberAxis.setTickUnit(new FastNumberTickUnit(getLinePeriodCount()));
}
}
else
{
if (getLineTicksLabelFormat() != null)
{
final DecimalFormat formatter = new DecimalFormat
(getLineTicksLabelFormat(), new DecimalFormatSymbols(getResourceBundleFactory().getLocale()));
numberAxis.setNumberFormatOverride(formatter);
}
}
}
else if (linesAxis instanceof DateAxis)
{
final DateAxis numberAxis = (DateAxis) linesAxis;
if (getLinePeriodCount() > 0 && getLineTimePeriod() != null)
{
if (getLineTicksLabelFormat() != null)
{
final SimpleDateFormat formatter = new SimpleDateFormat
(getLineTicksLabelFormat(), new DateFormatSymbols(getResourceBundleFactory().getLocale()));
numberAxis.setTickUnit
(new DateTickUnit(getDateUnitAsInt(getLineTimePeriod()), (int) getLinePeriodCount(), formatter));
}
else
{
numberAxis.setTickUnit
(new DateTickUnit(getDateUnitAsInt(getLineTimePeriod()), (int) getLinePeriodCount()));
}
}
else if (getRangeTickFormatString() != null)
{
final SimpleDateFormat formatter = new SimpleDateFormat
(getRangeTickFormatString(), new DateFormatSymbols(getResourceBundleFactory().getLocale()));
numberAxis.setDateFormatOverride(formatter);
}
}
if (linesAxis != null)
{
final Font labelFont = Font.decode(getLabelFont());
linesAxis.setLabelFont(labelFont);
linesAxis.setTickLabelFont(labelFont);
if (getLineTitleFont() != null)
{
linesAxis.setLabelFont(getLineTitleFont());
}
if (getLineTickFont() != null)
{
linesAxis.setTickLabelFont(getLineTickFont());
}
final Double lineRangeMin = getLineRangeMinimum();
if (lineRangeMin != null)
{
linesAxis.setLowerBound(lineRangeMin.doubleValue());
}
final Double lineRangeMax = getLineRangeMaximum();
if (lineRangeMax != null)
{
linesAxis.setUpperBound(lineRangeMax.doubleValue());
}
}
}
final XYLineAndShapeRenderer linesRenderer = (XYLineAndShapeRenderer) plot.getRenderer(1);
if (linesRenderer != null)
{
//set stroke with line width
linesRenderer.setStroke(translateLineStyle(getLineWidth(), getLineStyle()));
//hide shapes on line
linesRenderer.setShapesVisible(isMarkersVisible());
linesRenderer.setBaseShapesFilled(isMarkersVisible());
//set colors for each line
for (int i = 0; i < lineSeriesColor.size(); i++)
{
final String s = (String) lineSeriesColor.get(i);
linesRenderer.setSeriesPaint(i, parseColorFromString(s));
}
}
}
protected int getDateUnitAsInt(final Class domainTimePeriod)
{
if (Second.class.equals(domainTimePeriod))
{
return DateTickUnit.SECOND;
}
if (Minute.class.equals(domainTimePeriod))
{
return DateTickUnit.MINUTE;
}
if (Hour.class.equals(domainTimePeriod))
{
return DateTickUnit.HOUR;
}
if (Day.class.equals(domainTimePeriod))
{
return DateTickUnit.DAY;
}
if (Month.class.equals(domainTimePeriod))
{
return DateTickUnit.MONTH;
}
if (Year.class.equals(domainTimePeriod))
{
return DateTickUnit.YEAR;
}
if (Second.class.equals(domainTimePeriod))
{
return DateTickUnit.MILLISECOND;
}
return DateTickUnit.DAY;
}
}