/*******************************************************************************
* Copyright (c) 2008-2011 SWTChart project. All rights reserved.
*
* This code is distributed under the terms of the Eclipse Public License v1.0
* which is available at http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.swtchart.internal.series;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.Map.Entry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.swtchart.Chart;
import org.swtchart.IAxis;
import org.swtchart.ISeries;
import org.swtchart.ISeriesSet;
import org.swtchart.Range;
import org.swtchart.IAxis.Direction;
import org.swtchart.ISeries.SeriesType;
import org.swtchart.internal.axis.Axis;
import org.swtchart.internal.compress.CompressConfig;
import org.swtchart.internal.compress.ICompress;
/**
* A series container.
*/
public class SeriesSet implements ISeriesSet {
/** the chart */
private Chart chart;
/** the series */
private LinkedHashMap<String, Series> seriesMap;
/**
* Constructor.
*
* @param chart
* the chart
*/
public SeriesSet(Chart chart) {
this.chart = chart;
seriesMap = new LinkedHashMap<String, Series>();
}
/*
* @see ISeriesSet#createSeries(ISeries.SeriesType, String)
*/
public ISeries createSeries(SeriesType type, String id) {
if (id == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
return null; // to suppress warning...
}
String identifier = id.trim();
if ("".equals(identifier)) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
Series series = null;
if (type == SeriesType.BAR) {
series = new BarSeries(chart, identifier);
} else if (type == SeriesType.LINE) {
series = new LineSeries(chart, identifier);
} else {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
return null; // to suppress warning...
}
Series oldSeries = seriesMap.get(identifier);
if (oldSeries != null) {
oldSeries.dispose();
}
int[] xAxisIds = chart.getAxisSet().getXAxisIds();
int[] yAxisIds = chart.getAxisSet().getYAxisIds();
series.setXAxisId(xAxisIds[0]);
series.setYAxisId(yAxisIds[0]);
seriesMap.put(identifier, series);
Axis axis = (Axis) chart.getAxisSet().getXAxis(xAxisIds[0]);
if (axis != null) {
updateStackAndRiserData();
}
// legend will be shown if there is previously no series.
chart.updateLayout();
return series;
}
/*
* @see ISeriesSet#getSeries(String)
*/
public ISeries getSeries(String id) {
if (id == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
return seriesMap.get(id);
}
/*
* @see ISeriesSet#getSeries()
*/
public ISeries[] getSeries() {
Set<String> keys = seriesMap.keySet();
ISeries[] series = new ISeries[keys.size()];
int i = 0;
for (String key : keys) {
series[i++] = seriesMap.get(key);
}
return series;
}
/*
* @see ISeriesSet#deleteSeries(String)
*/
public void deleteSeries(String id) {
validateSeriesId(id);
seriesMap.get(id).dispose();
seriesMap.remove(id);
updateStackAndRiserData();
// legend will be hidden if this is the last series
chart.updateLayout();
}
/*
* @see ISeriesSet#bringForward(String)
*/
public void bringForward(String id) {
validateSeriesId(id);
String seriesId = null;
LinkedHashMap<String, Series> newSeriesMap = new LinkedHashMap<String, Series>();
for (Entry<String, Series> entry : seriesMap.entrySet()) {
if (entry.getKey().equals(id)) {
seriesId = id;
continue;
}
newSeriesMap.put(entry.getKey(), entry.getValue());
if (seriesId != null) {
newSeriesMap.put(seriesId, seriesMap.get(seriesId));
seriesId = null;
}
}
if (seriesId != null) {
newSeriesMap.put(seriesId, seriesMap.get(seriesId));
}
seriesMap = newSeriesMap;
updateStackAndRiserData();
chart.updateLayout();
}
/*
* @see ISeriesSet#bringToFront(String)
*/
public void bringToFront(String id) {
validateSeriesId(id);
Series series = seriesMap.get(id);
seriesMap.remove(id);
seriesMap.put(series.getId(), series);
updateStackAndRiserData();
chart.updateLayout();
}
/*
* @see ISeriesSet#sendBackward(String)
*/
public void sendBackward(String id) {
validateSeriesId(id);
String seriesId = null;
LinkedHashMap<String, Series> newSeriesMap = new LinkedHashMap<String, Series>();
for (Entry<String, Series> entry : seriesMap.entrySet()) {
if (!entry.getKey().equals(id) || seriesId == null) {
newSeriesMap.put(entry.getKey(), entry.getValue());
seriesId = entry.getKey();
continue;
}
newSeriesMap.remove(seriesId);
newSeriesMap.put(entry.getKey(), entry.getValue());
newSeriesMap.put(seriesId, seriesMap.get(seriesId));
}
seriesMap = newSeriesMap;
updateStackAndRiserData();
chart.updateLayout();
}
/*
* @see ISeriesSet#sendToBack(String)
*/
public void sendToBack(String id) {
validateSeriesId(id);
LinkedHashMap<String, Series> newSeriesMap = new LinkedHashMap<String, Series>();
newSeriesMap.put(id, seriesMap.get(id));
for (Entry<String, Series> entry : seriesMap.entrySet()) {
if (!entry.getKey().equals(id)) {
newSeriesMap.put(entry.getKey(), entry.getValue());
}
}
seriesMap = newSeriesMap;
updateStackAndRiserData();
chart.updateLayout();
}
/**
* Disposes the series.
*/
public void dispose() {
for (Entry<String, Series> entry : seriesMap.entrySet()) {
entry.getValue().dispose();
}
}
/**
* Validates the given series id.
*
* @param id
* the series id.
*/
private void validateSeriesId(String id) {
if (id == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
if (seriesMap.get(id) == null) {
throw new IllegalArgumentException("Given series id doesn't exist");
}
}
/**
* Compresses all series data.
*/
public void compressAllSeries() {
if (!chart.isCompressEnabled()) {
return;
}
CompressConfig config = new CompressConfig();
final int PRECISION = 2;
Point p = chart.getPlotArea().getSize();
int width = p.x * PRECISION;
int height = p.y * PRECISION;
config.setSizeInPixel(width, height);
for (ISeries series : getSeries()) {
int xAxisId = series.getXAxisId();
int yAxisId = series.getYAxisId();
IAxis xAxis = chart.getAxisSet().getXAxis(xAxisId);
IAxis yAxis = chart.getAxisSet().getYAxis(yAxisId);
if (xAxis == null || yAxis == null) {
continue;
}
Range xRange = xAxis.getRange();
Range yRange = yAxis.getRange();
if (xRange == null || yRange == null) {
continue;
}
double xMin = xRange.lower;
double xMax = xRange.upper;
double yMin = yRange.lower;
double yMax = yRange.upper;
config.setXLogScale(xAxis.isLogScaleEnabled());
config.setYLogScale(yAxis.isLogScaleEnabled());
double lower = xMin - (xMax - xMin) * 0.015;
double upper = xMax + (xMax - xMin) * 0.015;
if (xAxis.isLogScaleEnabled()) {
lower = ((Series) series).getXRange().lower;
}
config.setXRange(lower, upper);
lower = yMin - (yMax - yMin) * 0.015;
upper = yMax + (yMax - yMin) * 0.015;
if (yAxis.isLogScaleEnabled()) {
lower = ((Series) series).getYRange().lower;
}
config.setYRange(lower, upper);
ICompress compressor = ((Series) series).getCompressor();
compressor.compress(config);
}
}
/**
* Updates the compressor associated with the given axis.
* <p>
* In most cases, compressor is updated when series is changed. However,
* there is a case that compressor has to be updated with the changes in
* axis.
*
* @param axis
* the axis
*/
public void updateCompressor(Axis axis) {
for (ISeries series : getSeries()) {
int axisId = (axis.getDirection() == Direction.X) ? series
.getXAxisId() : series.getYAxisId();
if (axisId != axis.getId()) {
continue;
}
ICompress compressor = ((Series) series).getCompressor();
if (axis.isValidCategoryAxis()) {
String[] categorySeries = axis.getCategorySeries();
if (categorySeries == null) {
continue;
}
double[] xSeries = new double[categorySeries.length];
for (int i = 0; i < xSeries.length; i++) {
xSeries[i] = i;
}
compressor.setXSeries(xSeries);
} else if (((Series) series).getXSeries() != null) {
compressor.setXSeries(((Series) series).getXSeries());
}
}
compressAllSeries();
}
/**
* Updates the stack and riser data.
*/
public void updateStackAndRiserData() {
for (IAxis xAxis : chart.getAxisSet().getXAxes()) {
((Axis) xAxis).setNumRisers(0);
for (IAxis yAxis : chart.getAxisSet().getYAxes()) {
updateStackAndRiserData(xAxis, yAxis);
}
}
}
/**
* Updates the stack and riser data for given axes.
*
* @param xAxis
* the X axis
* @param yAxis
* the Y axis
*/
private void updateStackAndRiserData(IAxis xAxis, IAxis yAxis) {
int riserCnt = 0;
int stackRiserPosition = -1;
double[] stackBarSeries = null;
double[] stackLineSeries = null;
if (((Axis) xAxis).isValidCategoryAxis()) {
String[] categorySeries = xAxis.getCategorySeries();
if (categorySeries != null) {
int size = categorySeries.length;
stackBarSeries = new double[size];
stackLineSeries = new double[size];
}
}
for (ISeries series : getSeries()) {
if (series.getXAxisId() != xAxis.getId()
|| series.getYAxisId() != yAxis.getId()
|| !series.isVisible()) {
continue;
}
if (series.isStackEnabled()
&& !chart.getAxisSet().getYAxis(series.getYAxisId())
.isLogScaleEnabled()
&& ((Axis) xAxis).isValidCategoryAxis()) {
if (series.getType() == SeriesType.BAR) {
if (stackRiserPosition == -1) {
stackRiserPosition = riserCnt;
riserCnt++;
}
((BarSeries) series).setRiserIndex(((Axis) xAxis)
.getNumRisers() + stackRiserPosition);
setStackSeries(stackBarSeries, series);
} else if (series.getType() == SeriesType.LINE) {
setStackSeries(stackLineSeries, series);
}
} else {
if (series.getType() == SeriesType.BAR) {
((BarSeries) series).setRiserIndex(((Axis) xAxis)
.getNumRisers() + riserCnt++);
}
}
}
((Axis) xAxis).setNumRisers(((Axis) xAxis).getNumRisers() + riserCnt);
}
/**
* Sets the stack series.
*
* @param stackSeries
* the stack series
* @param series
* the series
*/
private void setStackSeries(double[] stackSeries, ISeries series) {
double[] ySeries = series.getYSeries();
if (ySeries == null || stackSeries == null) {
return;
}
for (int i = 0; i < stackSeries.length; i++) {
if (i > ySeries.length) {
break;
}
stackSeries[i] = new BigDecimal(new Double(stackSeries[i])
.toString()).add(
new BigDecimal(new Double(ySeries[i]).toString()))
.doubleValue();
}
double[] copiedStackSeries = new double[stackSeries.length];
System.arraycopy(stackSeries, 0, copiedStackSeries, 0,
stackSeries.length);
((Series) series).setStackSeries(copiedStackSeries);
}
}