/******************************************************************************* * 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 ); } }