/*******************************************************************************
* Copyright (c) 2008-2009 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, ISeries> seriesMap;
/**
* Constructor.
*
* @param chart
* the chart
*/
public SeriesSet ( Chart chart )
{
this.chart = chart;
seriesMap = new LinkedHashMap<String, ISeries> ();
}
/*
* @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 );
}
ISeries 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 = (Series)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 );
( (Series)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, ISeries> newSeriesMap = new LinkedHashMap<String, ISeries> ();
for ( Entry<String, ISeries> 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 );
ISeries 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, ISeries> newSeriesMap = new LinkedHashMap<String, ISeries> ();
for ( Entry<String, ISeries> 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, ISeries> newSeriesMap = new LinkedHashMap<String, ISeries> ();
newSeriesMap.put ( id, seriesMap.get ( id ) );
for ( Entry<String, ISeries> 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, ISeries> entry : seriesMap.entrySet () )
{
( (Series)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 () )
{
double[] xSeries = new double[axis.getCategorySeries ().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 () )
{
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 () )
{
int size = xAxis.getCategorySeries ().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 ( stackRiserPosition );
setStackSeries ( stackBarSeries, series );
}
else if ( series.getType () == SeriesType.LINE )
{
setStackSeries ( stackLineSeries, series );
}
}
else
{
if ( series.getType () == SeriesType.BAR )
{
( (BarSeries)series ).setRiserIndex ( riserCnt++ );
}
}
}
( (Axis)xAxis ).setNumRisers ( 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 )
{
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 );
}
}