0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ---------------------
0028: * DatasetUtilities.java
0029: * ---------------------
0030: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Andrzej Porebski (bug fix);
0034: * Jonathan Nash (bug fix);
0035: * Richard Atkinson;
0036: * Andreas Schroeder;
0037: *
0038: * $Id: DatasetUtilities.java,v 1.18.2.8 2007/06/12 14:54:30 mungady Exp $
0039: *
0040: * Changes (from 18-Sep-2001)
0041: * --------------------------
0042: * 18-Sep-2001 : Added standard header and fixed DOS encoding problem (DG);
0043: * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
0044: * 15-Nov-2001 : Moved to package com.jrefinery.data.* in the JCommon class
0045: * library (DG);
0046: * Changed to handle null values from datasets (DG);
0047: * Bug fix (thanks to Andrzej Porebski) - initial value now set
0048: * to positive or negative infinity when iterating (DG);
0049: * 22-Nov-2001 : Datasets with containing no data now return null for min and
0050: * max calculations (DG);
0051: * 13-Dec-2001 : Extended to handle HighLowDataset and IntervalXYDataset (DG);
0052: * 15-Feb-2002 : Added getMinimumStackedRangeValue() and
0053: * getMaximumStackedRangeValue() (DG);
0054: * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
0055: * 18-Mar-2002 : Fixed bug in min/max domain calculation for datasets that
0056: * implement the CategoryDataset interface AND the XYDataset
0057: * interface at the same time. Thanks to Jonathan Nash for the
0058: * fix (DG);
0059: * 23-Apr-2002 : Added getDomainExtent() and getRangeExtent() methods (DG);
0060: * 13-Jun-2002 : Modified range measurements to handle
0061: * IntervalCategoryDataset (DG);
0062: * 12-Jul-2002 : Method name change in DomainInfo interface (DG);
0063: * 30-Jul-2002 : Added pie dataset summation method (DG);
0064: * 01-Oct-2002 : Added a method for constructing an XYDataset from a Function2D
0065: * instance (DG);
0066: * 24-Oct-2002 : Amendments required following changes to the CategoryDataset
0067: * interface (DG);
0068: * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG);
0069: * 04-Mar-2003 : Added isEmpty(XYDataset) method (DG);
0070: * 05-Mar-2003 : Added a method for creating a CategoryDataset from a
0071: * KeyedValues instance (DG);
0072: * 15-May-2003 : Renamed isEmpty --> isEmptyOrNull (DG);
0073: * 25-Jun-2003 : Added limitPieDataset methods (RA);
0074: * 26-Jun-2003 : Modified getDomainExtent() method to accept null datasets (DG);
0075: * 27-Jul-2003 : Added getStackedRangeExtent(TableXYDataset data) (RA);
0076: * 18-Aug-2003 : getStackedRangeExtent(TableXYDataset data) now handles null
0077: * values (RA);
0078: * 02-Sep-2003 : Added method to check for null or empty PieDataset (DG);
0079: * 18-Sep-2003 : Fix for bug 803660 (getMaximumRangeValue for
0080: * CategoryDataset) (DG);
0081: * 20-Oct-2003 : Added getCumulativeRangeExtent() method (DG);
0082: * 09-Jan-2003 : Added argument checking code to the createCategoryDataset()
0083: * method (DG);
0084: * 23-Mar-2004 : Fixed bug in getMaximumStackedRangeValue() method (DG);
0085: * 31-Mar-2004 : Exposed the extent iteration algorithms to use one of them and
0086: * applied noninstantiation pattern (AS);
0087: * 11-May-2004 : Renamed getPieDatasetTotal --> calculatePieDatasetTotal (DG);
0088: * 15-Jul-2004 : Switched getX() with getXValue() and getY() with getYValue();
0089: * 24-Aug-2004 : Added argument checks to createCategoryDataset() method (DG);
0090: * 04-Oct-2004 : Renamed ArrayUtils --> ArrayUtilities (DG);
0091: * 06-Oct-2004 : Renamed findDomainExtent() --> findDomainBounds(),
0092: * findRangeExtent() --> findRangeBounds() (DG);
0093: * 07-Jan-2005 : Renamed findStackedRangeExtent() --> findStackedRangeBounds(),
0094: * findCumulativeRangeExtent() --> findCumulativeRangeBounds(),
0095: * iterateXYRangeExtent() --> iterateXYRangeBounds(),
0096: * removed deprecated methods (DG);
0097: * 03-Feb-2005 : The findStackedRangeBounds() methods now return null for
0098: * empty datasets (DG);
0099: * 03-Mar-2005 : Moved createNumberArray() and createNumberArray2D() methods
0100: * from DatasetUtilities --> DataUtilities (DG);
0101: * 22-Sep-2005 : Added new findStackedRangeBounds() method that takes base
0102: * argument (DG);
0103: * ------------- JFREECHART 1.0.x ---------------------------------------------
0104: * 15-Mar-2007 : Added calculateStackTotal() method (DG);
0105: *
0106: */
0107:
0108: package org.jfree.data.general;
0109:
0110: import java.util.ArrayList;
0111: import java.util.Iterator;
0112: import java.util.List;
0113:
0114: import org.jfree.data.DomainInfo;
0115: import org.jfree.data.KeyToGroupMap;
0116: import org.jfree.data.KeyedValues;
0117: import org.jfree.data.Range;
0118: import org.jfree.data.RangeInfo;
0119: import org.jfree.data.category.CategoryDataset;
0120: import org.jfree.data.category.DefaultCategoryDataset;
0121: import org.jfree.data.category.IntervalCategoryDataset;
0122: import org.jfree.data.function.Function2D;
0123: import org.jfree.data.xy.IntervalXYDataset;
0124: import org.jfree.data.xy.OHLCDataset;
0125: import org.jfree.data.xy.TableXYDataset;
0126: import org.jfree.data.xy.XYDataset;
0127: import org.jfree.data.xy.XYSeries;
0128: import org.jfree.data.xy.XYSeriesCollection;
0129: import org.jfree.util.ArrayUtilities;
0130:
0131: /**
0132: * A collection of useful static methods relating to datasets.
0133: */
0134: public final class DatasetUtilities {
0135:
0136: /**
0137: * Private constructor for non-instanceability.
0138: */
0139: private DatasetUtilities() {
0140: // now try to instantiate this ;-)
0141: }
0142:
0143: /**
0144: * Calculates the total of all the values in a {@link PieDataset}. If
0145: * the dataset contains negative or <code>null</code> values, they are
0146: * ignored.
0147: *
0148: * @param dataset the dataset (<code>null</code> not permitted).
0149: *
0150: * @return The total.
0151: */
0152: public static double calculatePieDatasetTotal(PieDataset dataset) {
0153: if (dataset == null) {
0154: throw new IllegalArgumentException(
0155: "Null 'dataset' argument.");
0156: }
0157: List keys = dataset.getKeys();
0158: double totalValue = 0;
0159: Iterator iterator = keys.iterator();
0160: while (iterator.hasNext()) {
0161: Comparable current = (Comparable) iterator.next();
0162: if (current != null) {
0163: Number value = dataset.getValue(current);
0164: double v = 0.0;
0165: if (value != null) {
0166: v = value.doubleValue();
0167: }
0168: if (v > 0) {
0169: totalValue = totalValue + v;
0170: }
0171: }
0172: }
0173: return totalValue;
0174: }
0175:
0176: /**
0177: * Creates a pie dataset from a table dataset by taking all the values
0178: * for a single row.
0179: *
0180: * @param dataset the dataset (<code>null</code> not permitted).
0181: * @param rowKey the row key.
0182: *
0183: * @return A pie dataset.
0184: */
0185: public static PieDataset createPieDatasetForRow(
0186: CategoryDataset dataset, Comparable rowKey) {
0187: int row = dataset.getRowIndex(rowKey);
0188: return createPieDatasetForRow(dataset, row);
0189: }
0190:
0191: /**
0192: * Creates a pie dataset from a table dataset by taking all the values
0193: * for a single row.
0194: *
0195: * @param dataset the dataset (<code>null</code> not permitted).
0196: * @param row the row (zero-based index).
0197: *
0198: * @return A pie dataset.
0199: */
0200: public static PieDataset createPieDatasetForRow(
0201: CategoryDataset dataset, int row) {
0202: DefaultPieDataset result = new DefaultPieDataset();
0203: int columnCount = dataset.getColumnCount();
0204: for (int current = 0; current < columnCount; current++) {
0205: Comparable columnKey = dataset.getColumnKey(current);
0206: result.setValue(columnKey, dataset.getValue(row, current));
0207: }
0208: return result;
0209: }
0210:
0211: /**
0212: * Creates a pie dataset from a table dataset by taking all the values
0213: * for a single column.
0214: *
0215: * @param dataset the dataset (<code>null</code> not permitted).
0216: * @param columnKey the column key.
0217: *
0218: * @return A pie dataset.
0219: */
0220: public static PieDataset createPieDatasetForColumn(
0221: CategoryDataset dataset, Comparable columnKey) {
0222: int column = dataset.getColumnIndex(columnKey);
0223: return createPieDatasetForColumn(dataset, column);
0224: }
0225:
0226: /**
0227: * Creates a pie dataset from a {@link CategoryDataset} by taking all the
0228: * values for a single column.
0229: *
0230: * @param dataset the dataset (<code>null</code> not permitted).
0231: * @param column the column (zero-based index).
0232: *
0233: * @return A pie dataset.
0234: */
0235: public static PieDataset createPieDatasetForColumn(
0236: CategoryDataset dataset, int column) {
0237: DefaultPieDataset result = new DefaultPieDataset();
0238: int rowCount = dataset.getRowCount();
0239: for (int i = 0; i < rowCount; i++) {
0240: Comparable rowKey = dataset.getRowKey(i);
0241: result.setValue(rowKey, dataset.getValue(i, column));
0242: }
0243: return result;
0244: }
0245:
0246: /**
0247: * Creates a new pie dataset based on the supplied dataset, but modified
0248: * by aggregating all the low value items (those whose value is lower
0249: * than the <code>percentThreshold</code>) into a single item with the
0250: * key "Other".
0251: *
0252: * @param source the source dataset (<code>null</code> not permitted).
0253: * @param key a new key for the aggregated items (<code>null</code> not
0254: * permitted).
0255: * @param minimumPercent the percent threshold.
0256: *
0257: * @return The pie dataset with (possibly) aggregated items.
0258: */
0259: public static PieDataset createConsolidatedPieDataset(
0260: PieDataset source, Comparable key, double minimumPercent) {
0261: return DatasetUtilities.createConsolidatedPieDataset(source,
0262: key, minimumPercent, 2);
0263: }
0264:
0265: /**
0266: * Creates a new pie dataset based on the supplied dataset, but modified
0267: * by aggregating all the low value items (those whose value is lower
0268: * than the <code>percentThreshold</code>) into a single item. The
0269: * aggregated items are assigned the specified key. Aggregation only
0270: * occurs if there are at least <code>minItems</code> items to aggregate.
0271: *
0272: * @param source the source dataset (<code>null</code> not permitted).
0273: * @param key the key to represent the aggregated items.
0274: * @param minimumPercent the percent threshold (ten percent is 0.10).
0275: * @param minItems only aggregate low values if there are at least this
0276: * many.
0277: *
0278: * @return The pie dataset with (possibly) aggregated items.
0279: */
0280: public static PieDataset createConsolidatedPieDataset(
0281: PieDataset source, Comparable key, double minimumPercent,
0282: int minItems) {
0283:
0284: DefaultPieDataset result = new DefaultPieDataset();
0285: double total = DatasetUtilities
0286: .calculatePieDatasetTotal(source);
0287:
0288: // Iterate and find all keys below threshold percentThreshold
0289: List keys = source.getKeys();
0290: ArrayList otherKeys = new ArrayList();
0291: Iterator iterator = keys.iterator();
0292: while (iterator.hasNext()) {
0293: Comparable currentKey = (Comparable) iterator.next();
0294: Number dataValue = source.getValue(currentKey);
0295: if (dataValue != null) {
0296: double value = dataValue.doubleValue();
0297: if (value / total < minimumPercent) {
0298: otherKeys.add(currentKey);
0299: }
0300: }
0301: }
0302:
0303: // Create new dataset with keys above threshold percentThreshold
0304: iterator = keys.iterator();
0305: double otherValue = 0;
0306: while (iterator.hasNext()) {
0307: Comparable currentKey = (Comparable) iterator.next();
0308: Number dataValue = source.getValue(currentKey);
0309: if (dataValue != null) {
0310: if (otherKeys.contains(currentKey)
0311: && otherKeys.size() >= minItems) {
0312: // Do not add key to dataset
0313: otherValue += dataValue.doubleValue();
0314: } else {
0315: // Add key to dataset
0316: result.setValue(currentKey, dataValue);
0317: }
0318: }
0319: }
0320: // Add other category if applicable
0321: if (otherKeys.size() >= minItems) {
0322: result.setValue(key, otherValue);
0323: }
0324: return result;
0325: }
0326:
0327: /**
0328: * Creates a {@link CategoryDataset} that contains a copy of the data in an
0329: * array (instances of <code>Double</code> are created to represent the
0330: * data items).
0331: * <p>
0332: * Row and column keys are created by appending 0, 1, 2, ... to the
0333: * supplied prefixes.
0334: *
0335: * @param rowKeyPrefix the row key prefix.
0336: * @param columnKeyPrefix the column key prefix.
0337: * @param data the data.
0338: *
0339: * @return The dataset.
0340: */
0341: public static CategoryDataset createCategoryDataset(
0342: String rowKeyPrefix, String columnKeyPrefix, double[][] data) {
0343:
0344: DefaultCategoryDataset result = new DefaultCategoryDataset();
0345: for (int r = 0; r < data.length; r++) {
0346: String rowKey = rowKeyPrefix + (r + 1);
0347: for (int c = 0; c < data[r].length; c++) {
0348: String columnKey = columnKeyPrefix + (c + 1);
0349: result.addValue(new Double(data[r][c]), rowKey,
0350: columnKey);
0351: }
0352: }
0353: return result;
0354:
0355: }
0356:
0357: /**
0358: * Creates a {@link CategoryDataset} that contains a copy of the data in
0359: * an array.
0360: * <p>
0361: * Row and column keys are created by appending 0, 1, 2, ... to the
0362: * supplied prefixes.
0363: *
0364: * @param rowKeyPrefix the row key prefix.
0365: * @param columnKeyPrefix the column key prefix.
0366: * @param data the data.
0367: *
0368: * @return The dataset.
0369: */
0370: public static CategoryDataset createCategoryDataset(
0371: String rowKeyPrefix, String columnKeyPrefix, Number[][] data) {
0372:
0373: DefaultCategoryDataset result = new DefaultCategoryDataset();
0374: for (int r = 0; r < data.length; r++) {
0375: String rowKey = rowKeyPrefix + (r + 1);
0376: for (int c = 0; c < data[r].length; c++) {
0377: String columnKey = columnKeyPrefix + (c + 1);
0378: result.addValue(data[r][c], rowKey, columnKey);
0379: }
0380: }
0381: return result;
0382:
0383: }
0384:
0385: /**
0386: * Creates a {@link CategoryDataset} that contains a copy of the data in
0387: * an array (instances of <code>Double</code> are created to represent the
0388: * data items).
0389: * <p>
0390: * Row and column keys are taken from the supplied arrays.
0391: *
0392: * @param rowKeys the row keys (<code>null</code> not permitted).
0393: * @param columnKeys the column keys (<code>null</code> not permitted).
0394: * @param data the data.
0395: *
0396: * @return The dataset.
0397: */
0398: public static CategoryDataset createCategoryDataset(
0399: Comparable[] rowKeys, Comparable[] columnKeys,
0400: double[][] data) {
0401:
0402: // check arguments...
0403: if (rowKeys == null) {
0404: throw new IllegalArgumentException(
0405: "Null 'rowKeys' argument.");
0406: }
0407: if (columnKeys == null) {
0408: throw new IllegalArgumentException(
0409: "Null 'columnKeys' argument.");
0410: }
0411: if (ArrayUtilities.hasDuplicateItems(rowKeys)) {
0412: throw new IllegalArgumentException(
0413: "Duplicate items in 'rowKeys'.");
0414: }
0415: if (ArrayUtilities.hasDuplicateItems(columnKeys)) {
0416: throw new IllegalArgumentException(
0417: "Duplicate items in 'columnKeys'.");
0418: }
0419: if (rowKeys.length != data.length) {
0420: throw new IllegalArgumentException(
0421: "The number of row keys does not match the number of rows in "
0422: + "the data array.");
0423: }
0424: int columnCount = 0;
0425: for (int r = 0; r < data.length; r++) {
0426: columnCount = Math.max(columnCount, data[r].length);
0427: }
0428: if (columnKeys.length != columnCount) {
0429: throw new IllegalArgumentException(
0430: "The number of column keys does not match the number of "
0431: + "columns in the data array.");
0432: }
0433:
0434: // now do the work...
0435: DefaultCategoryDataset result = new DefaultCategoryDataset();
0436: for (int r = 0; r < data.length; r++) {
0437: Comparable rowKey = rowKeys[r];
0438: for (int c = 0; c < data[r].length; c++) {
0439: Comparable columnKey = columnKeys[c];
0440: result.addValue(new Double(data[r][c]), rowKey,
0441: columnKey);
0442: }
0443: }
0444: return result;
0445:
0446: }
0447:
0448: /**
0449: * Creates a {@link CategoryDataset} by copying the data from the supplied
0450: * {@link KeyedValues} instance.
0451: *
0452: * @param rowKey the row key (<code>null</code> not permitted).
0453: * @param rowData the row data (<code>null</code> not permitted).
0454: *
0455: * @return A dataset.
0456: */
0457: public static CategoryDataset createCategoryDataset(
0458: Comparable rowKey, KeyedValues rowData) {
0459:
0460: if (rowKey == null) {
0461: throw new IllegalArgumentException(
0462: "Null 'rowKey' argument.");
0463: }
0464: if (rowData == null) {
0465: throw new IllegalArgumentException(
0466: "Null 'rowData' argument.");
0467: }
0468: DefaultCategoryDataset result = new DefaultCategoryDataset();
0469: for (int i = 0; i < rowData.getItemCount(); i++) {
0470: result.addValue(rowData.getValue(i), rowKey, rowData
0471: .getKey(i));
0472: }
0473: return result;
0474:
0475: }
0476:
0477: /**
0478: * Creates an {@link XYDataset} by sampling the specified function over a
0479: * fixed range.
0480: *
0481: * @param f the function (<code>null</code> not permitted).
0482: * @param start the start value for the range.
0483: * @param end the end value for the range.
0484: * @param samples the number of sample points (must be > 1).
0485: * @param seriesKey the key to give the resulting series
0486: * (<code>null</code> not permitted).
0487: *
0488: * @return A dataset.
0489: */
0490: public static XYDataset sampleFunction2D(Function2D f,
0491: double start, double end, int samples, Comparable seriesKey) {
0492:
0493: if (f == null) {
0494: throw new IllegalArgumentException("Null 'f' argument.");
0495: }
0496: if (seriesKey == null) {
0497: throw new IllegalArgumentException(
0498: "Null 'seriesKey' argument.");
0499: }
0500: if (start >= end) {
0501: throw new IllegalArgumentException(
0502: "Requires 'start' < 'end'.");
0503: }
0504: if (samples < 2) {
0505: throw new IllegalArgumentException("Requires 'samples' > 1");
0506: }
0507:
0508: XYSeries series = new XYSeries(seriesKey);
0509: double step = (end - start) / samples;
0510: for (int i = 0; i <= samples; i++) {
0511: double x = start + (step * i);
0512: series.add(x, f.getValue(x));
0513: }
0514: XYSeriesCollection collection = new XYSeriesCollection(series);
0515: return collection;
0516:
0517: }
0518:
0519: /**
0520: * Returns <code>true</code> if the dataset is empty (or <code>null</code>),
0521: * and <code>false</code> otherwise.
0522: *
0523: * @param dataset the dataset (<code>null</code> permitted).
0524: *
0525: * @return A boolean.
0526: */
0527: public static boolean isEmptyOrNull(PieDataset dataset) {
0528:
0529: if (dataset == null) {
0530: return true;
0531: }
0532:
0533: int itemCount = dataset.getItemCount();
0534: if (itemCount == 0) {
0535: return true;
0536: }
0537:
0538: for (int item = 0; item < itemCount; item++) {
0539: Number y = dataset.getValue(item);
0540: if (y != null) {
0541: double yy = y.doubleValue();
0542: if (yy > 0.0) {
0543: return false;
0544: }
0545: }
0546: }
0547:
0548: return true;
0549:
0550: }
0551:
0552: /**
0553: * Returns <code>true</code> if the dataset is empty (or <code>null</code>),
0554: * and <code>false</code> otherwise.
0555: *
0556: * @param dataset the dataset (<code>null</code> permitted).
0557: *
0558: * @return A boolean.
0559: */
0560: public static boolean isEmptyOrNull(CategoryDataset dataset) {
0561:
0562: if (dataset == null) {
0563: return true;
0564: }
0565:
0566: int rowCount = dataset.getRowCount();
0567: int columnCount = dataset.getColumnCount();
0568: if (rowCount == 0 || columnCount == 0) {
0569: return true;
0570: }
0571:
0572: for (int r = 0; r < rowCount; r++) {
0573: for (int c = 0; c < columnCount; c++) {
0574: if (dataset.getValue(r, c) != null) {
0575: return false;
0576: }
0577:
0578: }
0579: }
0580:
0581: return true;
0582:
0583: }
0584:
0585: /**
0586: * Returns <code>true</code> if the dataset is empty (or <code>null</code>),
0587: * and <code>false</code> otherwise.
0588: *
0589: * @param dataset the dataset (<code>null</code> permitted).
0590: *
0591: * @return A boolean.
0592: */
0593: public static boolean isEmptyOrNull(XYDataset dataset) {
0594:
0595: boolean result = true;
0596:
0597: if (dataset != null) {
0598: for (int s = 0; s < dataset.getSeriesCount(); s++) {
0599: if (dataset.getItemCount(s) > 0) {
0600: result = false;
0601: continue;
0602: }
0603: }
0604: }
0605:
0606: return result;
0607:
0608: }
0609:
0610: /**
0611: * Returns the range of values in the domain (x-values) of a dataset.
0612: *
0613: * @param dataset the dataset (<code>null</code> not permitted).
0614: *
0615: * @return The range of values (possibly <code>null</code>).
0616: */
0617: public static Range findDomainBounds(XYDataset dataset) {
0618: return findDomainBounds(dataset, true);
0619: }
0620:
0621: /**
0622: * Returns the range of values in the domain (x-values) of a dataset.
0623: *
0624: * @param dataset the dataset (<code>null</code> not permitted).
0625: * @param includeInterval determines whether or not the x-interval is taken
0626: * into account (only applies if the dataset is an
0627: * {@link IntervalXYDataset}).
0628: *
0629: * @return The range of values (possibly <code>null</code>).
0630: */
0631: public static Range findDomainBounds(XYDataset dataset,
0632: boolean includeInterval) {
0633:
0634: if (dataset == null) {
0635: throw new IllegalArgumentException(
0636: "Null 'dataset' argument.");
0637: }
0638:
0639: Range result = null;
0640: // if the dataset implements DomainInfo, life is easier
0641: if (dataset instanceof DomainInfo) {
0642: DomainInfo info = (DomainInfo) dataset;
0643: result = info.getDomainBounds(includeInterval);
0644: } else {
0645: result = iterateDomainBounds(dataset, includeInterval);
0646: }
0647: return result;
0648:
0649: }
0650:
0651: /**
0652: * Iterates over the items in an {@link XYDataset} to find
0653: * the range of x-values.
0654: *
0655: * @param dataset the dataset (<code>null</code> not permitted).
0656: *
0657: * @return The range (possibly <code>null</code>).
0658: */
0659: public static Range iterateDomainBounds(XYDataset dataset) {
0660: return iterateDomainBounds(dataset, true);
0661: }
0662:
0663: /**
0664: * Iterates over the items in an {@link XYDataset} to find
0665: * the range of x-values.
0666: *
0667: * @param dataset the dataset (<code>null</code> not permitted).
0668: * @param includeInterval a flag that determines, for an IntervalXYDataset,
0669: * whether the x-interval or just the x-value is
0670: * used to determine the overall range.
0671: *
0672: * @return The range (possibly <code>null</code>).
0673: */
0674: public static Range iterateDomainBounds(XYDataset dataset,
0675: boolean includeInterval) {
0676: if (dataset == null) {
0677: throw new IllegalArgumentException(
0678: "Null 'dataset' argument.");
0679: }
0680: double minimum = Double.POSITIVE_INFINITY;
0681: double maximum = Double.NEGATIVE_INFINITY;
0682: int seriesCount = dataset.getSeriesCount();
0683: double lvalue;
0684: double uvalue;
0685: if (includeInterval && dataset instanceof IntervalXYDataset) {
0686: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
0687: for (int series = 0; series < seriesCount; series++) {
0688: int itemCount = dataset.getItemCount(series);
0689: for (int item = 0; item < itemCount; item++) {
0690: lvalue = intervalXYData
0691: .getStartXValue(series, item);
0692: uvalue = intervalXYData.getEndXValue(series, item);
0693: minimum = Math.min(minimum, lvalue);
0694: maximum = Math.max(maximum, uvalue);
0695: }
0696: }
0697: } else {
0698: for (int series = 0; series < seriesCount; series++) {
0699: int itemCount = dataset.getItemCount(series);
0700: for (int item = 0; item < itemCount; item++) {
0701: lvalue = dataset.getXValue(series, item);
0702: uvalue = lvalue;
0703: minimum = Math.min(minimum, lvalue);
0704: maximum = Math.max(maximum, uvalue);
0705: }
0706: }
0707: }
0708: if (minimum > maximum) {
0709: return null;
0710: } else {
0711: return new Range(minimum, maximum);
0712: }
0713: }
0714:
0715: /**
0716: * Returns the range of values in the range for the dataset.
0717: *
0718: * @param dataset the dataset (<code>null</code> not permitted).
0719: *
0720: * @return The range (possibly <code>null</code>).
0721: */
0722: public static Range findRangeBounds(CategoryDataset dataset) {
0723: return findRangeBounds(dataset, true);
0724: }
0725:
0726: /**
0727: * Returns the range of values in the range for the dataset.
0728: *
0729: * @param dataset the dataset (<code>null</code> not permitted).
0730: * @param includeInterval a flag that determines whether or not the
0731: * y-interval is taken into account.
0732: *
0733: * @return The range (possibly <code>null</code>).
0734: */
0735: public static Range findRangeBounds(CategoryDataset dataset,
0736: boolean includeInterval) {
0737: if (dataset == null) {
0738: throw new IllegalArgumentException(
0739: "Null 'dataset' argument.");
0740: }
0741: Range result = null;
0742: if (dataset instanceof RangeInfo) {
0743: RangeInfo info = (RangeInfo) dataset;
0744: result = info.getRangeBounds(includeInterval);
0745: } else {
0746: result = iterateCategoryRangeBounds(dataset,
0747: includeInterval);
0748: }
0749: return result;
0750: }
0751:
0752: /**
0753: * Returns the range of values in the range for the dataset. This method
0754: * is the partner for the {@link #findDomainBounds(XYDataset)} method.
0755: *
0756: * @param dataset the dataset (<code>null</code> not permitted).
0757: *
0758: * @return The range (possibly <code>null</code>).
0759: */
0760: public static Range findRangeBounds(XYDataset dataset) {
0761: return findRangeBounds(dataset, true);
0762: }
0763:
0764: /**
0765: * Returns the range of values in the range for the dataset. This method
0766: * is the partner for the {@link #findDomainBounds(XYDataset)} method.
0767: *
0768: * @param dataset the dataset (<code>null</code> not permitted).
0769: * @param includeInterval a flag that determines whether or not the
0770: * y-interval is taken into account.
0771: *
0772: *
0773: * @return The range (possibly <code>null</code>).
0774: */
0775: public static Range findRangeBounds(XYDataset dataset,
0776: boolean includeInterval) {
0777: if (dataset == null) {
0778: throw new IllegalArgumentException(
0779: "Null 'dataset' argument.");
0780: }
0781: Range result = null;
0782: if (dataset instanceof RangeInfo) {
0783: RangeInfo info = (RangeInfo) dataset;
0784: result = info.getRangeBounds(includeInterval);
0785: } else {
0786: result = iterateXYRangeBounds(dataset);
0787: }
0788: return result;
0789: }
0790:
0791: /**
0792: * Iterates over the data item of the category dataset to find
0793: * the range bounds.
0794: *
0795: * @param dataset the dataset (<code>null</code> not permitted).
0796: * @param includeInterval a flag that determines whether or not the
0797: * y-interval is taken into account.
0798: *
0799: * @return The range (possibly <code>null</code>).
0800: */
0801: public static Range iterateCategoryRangeBounds(
0802: CategoryDataset dataset, boolean includeInterval) {
0803: double minimum = Double.POSITIVE_INFINITY;
0804: double maximum = Double.NEGATIVE_INFINITY;
0805: boolean interval = includeInterval
0806: && dataset instanceof IntervalCategoryDataset;
0807: int rowCount = dataset.getRowCount();
0808: int columnCount = dataset.getColumnCount();
0809: for (int row = 0; row < rowCount; row++) {
0810: for (int column = 0; column < columnCount; column++) {
0811: Number lvalue;
0812: Number uvalue;
0813: if (interval) {
0814: IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset;
0815: lvalue = icd.getStartValue(row, column);
0816: uvalue = icd.getEndValue(row, column);
0817: } else {
0818: lvalue = dataset.getValue(row, column);
0819: uvalue = lvalue;
0820: }
0821: if (lvalue != null) {
0822: minimum = Math.min(minimum, lvalue.doubleValue());
0823: }
0824: if (uvalue != null) {
0825: maximum = Math.max(maximum, uvalue.doubleValue());
0826: }
0827: }
0828: }
0829: if (minimum == Double.POSITIVE_INFINITY) {
0830: return null;
0831: } else {
0832: return new Range(minimum, maximum);
0833: }
0834: }
0835:
0836: /**
0837: * Iterates over the data item of the xy dataset to find
0838: * the range bounds.
0839: *
0840: * @param dataset the dataset (<code>null</code> not permitted).
0841: *
0842: * @return The range (possibly <code>null</code>).
0843: */
0844: public static Range iterateXYRangeBounds(XYDataset dataset) {
0845: double minimum = Double.POSITIVE_INFINITY;
0846: double maximum = Double.NEGATIVE_INFINITY;
0847: int seriesCount = dataset.getSeriesCount();
0848: for (int series = 0; series < seriesCount; series++) {
0849: int itemCount = dataset.getItemCount(series);
0850: for (int item = 0; item < itemCount; item++) {
0851: double lvalue;
0852: double uvalue;
0853: if (dataset instanceof IntervalXYDataset) {
0854: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
0855: lvalue = intervalXYData
0856: .getStartYValue(series, item);
0857: uvalue = intervalXYData.getEndYValue(series, item);
0858: } else if (dataset instanceof OHLCDataset) {
0859: OHLCDataset highLowData = (OHLCDataset) dataset;
0860: lvalue = highLowData.getLowValue(series, item);
0861: uvalue = highLowData.getHighValue(series, item);
0862: } else {
0863: lvalue = dataset.getYValue(series, item);
0864: uvalue = lvalue;
0865: }
0866: if (!Double.isNaN(lvalue)) {
0867: minimum = Math.min(minimum, lvalue);
0868: }
0869: if (!Double.isNaN(uvalue)) {
0870: maximum = Math.max(maximum, uvalue);
0871: }
0872: }
0873: }
0874: if (minimum == Double.POSITIVE_INFINITY) {
0875: return null;
0876: } else {
0877: return new Range(minimum, maximum);
0878: }
0879: }
0880:
0881: /**
0882: * Finds the minimum domain (or X) value for the specified dataset. This
0883: * is easy if the dataset implements the {@link DomainInfo} interface (a
0884: * good idea if there is an efficient way to determine the minimum value).
0885: * Otherwise, it involves iterating over the entire data-set.
0886: * <p>
0887: * Returns <code>null</code> if all the data values in the dataset are
0888: * <code>null</code>.
0889: *
0890: * @param dataset the dataset (<code>null</code> not permitted).
0891: *
0892: * @return The minimum value (possibly <code>null</code>).
0893: */
0894: public static Number findMinimumDomainValue(XYDataset dataset) {
0895: if (dataset == null) {
0896: throw new IllegalArgumentException(
0897: "Null 'dataset' argument.");
0898: }
0899: Number result = null;
0900: // if the dataset implements DomainInfo, life is easy
0901: if (dataset instanceof DomainInfo) {
0902: DomainInfo info = (DomainInfo) dataset;
0903: return new Double(info.getDomainLowerBound(true));
0904: } else {
0905: double minimum = Double.POSITIVE_INFINITY;
0906: int seriesCount = dataset.getSeriesCount();
0907: for (int series = 0; series < seriesCount; series++) {
0908: int itemCount = dataset.getItemCount(series);
0909: for (int item = 0; item < itemCount; item++) {
0910:
0911: double value;
0912: if (dataset instanceof IntervalXYDataset) {
0913: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
0914: value = intervalXYData.getStartXValue(series,
0915: item);
0916: } else {
0917: value = dataset.getXValue(series, item);
0918: }
0919: if (!Double.isNaN(value)) {
0920: minimum = Math.min(minimum, value);
0921: }
0922:
0923: }
0924: }
0925: if (minimum == Double.POSITIVE_INFINITY) {
0926: result = null;
0927: } else {
0928: result = new Double(minimum);
0929: }
0930: }
0931:
0932: return result;
0933: }
0934:
0935: /**
0936: * Returns the maximum domain value for the specified dataset. This is
0937: * easy if the dataset implements the {@link DomainInfo} interface (a good
0938: * idea if there is an efficient way to determine the maximum value).
0939: * Otherwise, it involves iterating over the entire data-set. Returns
0940: * <code>null</code> if all the data values in the dataset are
0941: * <code>null</code>.
0942: *
0943: * @param dataset the dataset (<code>null</code> not permitted).
0944: *
0945: * @return The maximum value (possibly <code>null</code>).
0946: */
0947: public static Number findMaximumDomainValue(XYDataset dataset) {
0948: if (dataset == null) {
0949: throw new IllegalArgumentException(
0950: "Null 'dataset' argument.");
0951: }
0952: Number result = null;
0953: // if the dataset implements DomainInfo, life is easy
0954: if (dataset instanceof DomainInfo) {
0955: DomainInfo info = (DomainInfo) dataset;
0956: return new Double(info.getDomainUpperBound(true));
0957: }
0958:
0959: // hasn't implemented DomainInfo, so iterate...
0960: else {
0961: double maximum = Double.NEGATIVE_INFINITY;
0962: int seriesCount = dataset.getSeriesCount();
0963: for (int series = 0; series < seriesCount; series++) {
0964: int itemCount = dataset.getItemCount(series);
0965: for (int item = 0; item < itemCount; item++) {
0966:
0967: double value;
0968: if (dataset instanceof IntervalXYDataset) {
0969: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
0970: value = intervalXYData.getEndXValue(series,
0971: item);
0972: } else {
0973: value = dataset.getXValue(series, item);
0974: }
0975: if (!Double.isNaN(value)) {
0976: maximum = Math.max(maximum, value);
0977: }
0978: }
0979: }
0980: if (maximum == Double.NEGATIVE_INFINITY) {
0981: result = null;
0982: } else {
0983: result = new Double(maximum);
0984: }
0985:
0986: }
0987:
0988: return result;
0989: }
0990:
0991: /**
0992: * Returns the minimum range value for the specified dataset. This is
0993: * easy if the dataset implements the {@link RangeInfo} interface (a good
0994: * idea if there is an efficient way to determine the minimum value).
0995: * Otherwise, it involves iterating over the entire data-set. Returns
0996: * <code>null</code> if all the data values in the dataset are
0997: * <code>null</code>.
0998: *
0999: * @param dataset the dataset (<code>null</code> not permitted).
1000: *
1001: * @return The minimum value (possibly <code>null</code>).
1002: */
1003: public static Number findMinimumRangeValue(CategoryDataset dataset) {
1004:
1005: // check parameters...
1006: if (dataset == null) {
1007: throw new IllegalArgumentException(
1008: "Null 'dataset' argument.");
1009: }
1010:
1011: // work out the minimum value...
1012: if (dataset instanceof RangeInfo) {
1013: RangeInfo info = (RangeInfo) dataset;
1014: return new Double(info.getRangeLowerBound(true));
1015: }
1016:
1017: // hasn't implemented RangeInfo, so we'll have to iterate...
1018: else {
1019: double minimum = Double.POSITIVE_INFINITY;
1020: int seriesCount = dataset.getRowCount();
1021: int itemCount = dataset.getColumnCount();
1022: for (int series = 0; series < seriesCount; series++) {
1023: for (int item = 0; item < itemCount; item++) {
1024: Number value;
1025: if (dataset instanceof IntervalCategoryDataset) {
1026: IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset;
1027: value = icd.getStartValue(series, item);
1028: } else {
1029: value = dataset.getValue(series, item);
1030: }
1031: if (value != null) {
1032: minimum = Math
1033: .min(minimum, value.doubleValue());
1034: }
1035: }
1036: }
1037: if (minimum == Double.POSITIVE_INFINITY) {
1038: return null;
1039: } else {
1040: return new Double(minimum);
1041: }
1042:
1043: }
1044:
1045: }
1046:
1047: /**
1048: * Returns the minimum range value for the specified dataset. This is
1049: * easy if the dataset implements the {@link RangeInfo} interface (a good
1050: * idea if there is an efficient way to determine the minimum value).
1051: * Otherwise, it involves iterating over the entire data-set. Returns
1052: * <code>null</code> if all the data values in the dataset are
1053: * <code>null</code>.
1054: *
1055: * @param dataset the dataset (<code>null</code> not permitted).
1056: *
1057: * @return The minimum value (possibly <code>null</code>).
1058: */
1059: public static Number findMinimumRangeValue(XYDataset dataset) {
1060:
1061: if (dataset == null) {
1062: throw new IllegalArgumentException(
1063: "Null 'dataset' argument.");
1064: }
1065:
1066: // work out the minimum value...
1067: if (dataset instanceof RangeInfo) {
1068: RangeInfo info = (RangeInfo) dataset;
1069: return new Double(info.getRangeLowerBound(true));
1070: }
1071:
1072: // hasn't implemented RangeInfo, so we'll have to iterate...
1073: else {
1074: double minimum = Double.POSITIVE_INFINITY;
1075: int seriesCount = dataset.getSeriesCount();
1076: for (int series = 0; series < seriesCount; series++) {
1077: int itemCount = dataset.getItemCount(series);
1078: for (int item = 0; item < itemCount; item++) {
1079:
1080: double value;
1081: if (dataset instanceof IntervalXYDataset) {
1082: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
1083: value = intervalXYData.getStartYValue(series,
1084: item);
1085: } else if (dataset instanceof OHLCDataset) {
1086: OHLCDataset highLowData = (OHLCDataset) dataset;
1087: value = highLowData.getLowValue(series, item);
1088: } else {
1089: value = dataset.getYValue(series, item);
1090: }
1091: if (!Double.isNaN(value)) {
1092: minimum = Math.min(minimum, value);
1093: }
1094:
1095: }
1096: }
1097: if (minimum == Double.POSITIVE_INFINITY) {
1098: return null;
1099: } else {
1100: return new Double(minimum);
1101: }
1102:
1103: }
1104:
1105: }
1106:
1107: /**
1108: * Returns the maximum range value for the specified dataset. This is easy
1109: * if the dataset implements the {@link RangeInfo} interface (a good idea
1110: * if there is an efficient way to determine the maximum value).
1111: * Otherwise, it involves iterating over the entire data-set. Returns
1112: * <code>null</code> if all the data values are <code>null</code>.
1113: *
1114: * @param dataset the dataset (<code>null</code> not permitted).
1115: *
1116: * @return The maximum value (possibly <code>null</code>).
1117: */
1118: public static Number findMaximumRangeValue(CategoryDataset dataset) {
1119:
1120: if (dataset == null) {
1121: throw new IllegalArgumentException(
1122: "Null 'dataset' argument.");
1123: }
1124:
1125: // work out the minimum value...
1126: if (dataset instanceof RangeInfo) {
1127: RangeInfo info = (RangeInfo) dataset;
1128: return new Double(info.getRangeUpperBound(true));
1129: }
1130:
1131: // hasn't implemented RangeInfo, so we'll have to iterate...
1132: else {
1133:
1134: double maximum = Double.NEGATIVE_INFINITY;
1135: int seriesCount = dataset.getRowCount();
1136: int itemCount = dataset.getColumnCount();
1137: for (int series = 0; series < seriesCount; series++) {
1138: for (int item = 0; item < itemCount; item++) {
1139: Number value;
1140: if (dataset instanceof IntervalCategoryDataset) {
1141: IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset;
1142: value = icd.getEndValue(series, item);
1143: } else {
1144: value = dataset.getValue(series, item);
1145: }
1146: if (value != null) {
1147: maximum = Math
1148: .max(maximum, value.doubleValue());
1149: }
1150: }
1151: }
1152: if (maximum == Double.NEGATIVE_INFINITY) {
1153: return null;
1154: } else {
1155: return new Double(maximum);
1156: }
1157:
1158: }
1159:
1160: }
1161:
1162: /**
1163: * Returns the maximum range value for the specified dataset. This is
1164: * easy if the dataset implements the {@link RangeInfo} interface (a good
1165: * idea if there is an efficient way to determine the maximum value).
1166: * Otherwise, it involves iterating over the entire data-set. Returns
1167: * <code>null</code> if all the data values are <code>null</code>.
1168: *
1169: * @param dataset the dataset (<code>null</code> not permitted).
1170: *
1171: * @return The maximum value (possibly <code>null</code>).
1172: */
1173: public static Number findMaximumRangeValue(XYDataset dataset) {
1174:
1175: if (dataset == null) {
1176: throw new IllegalArgumentException(
1177: "Null 'dataset' argument.");
1178: }
1179:
1180: // work out the minimum value...
1181: if (dataset instanceof RangeInfo) {
1182: RangeInfo info = (RangeInfo) dataset;
1183: return new Double(info.getRangeUpperBound(true));
1184: }
1185:
1186: // hasn't implemented RangeInfo, so we'll have to iterate...
1187: else {
1188:
1189: double maximum = Double.NEGATIVE_INFINITY;
1190: int seriesCount = dataset.getSeriesCount();
1191: for (int series = 0; series < seriesCount; series++) {
1192: int itemCount = dataset.getItemCount(series);
1193: for (int item = 0; item < itemCount; item++) {
1194: double value;
1195: if (dataset instanceof IntervalXYDataset) {
1196: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
1197: value = intervalXYData.getEndYValue(series,
1198: item);
1199: } else if (dataset instanceof OHLCDataset) {
1200: OHLCDataset highLowData = (OHLCDataset) dataset;
1201: value = highLowData.getHighValue(series, item);
1202: } else {
1203: value = dataset.getYValue(series, item);
1204: }
1205: if (!Double.isNaN(value)) {
1206: maximum = Math.max(maximum, value);
1207: }
1208: }
1209: }
1210: if (maximum == Double.NEGATIVE_INFINITY) {
1211: return null;
1212: } else {
1213: return new Double(maximum);
1214: }
1215:
1216: }
1217:
1218: }
1219:
1220: /**
1221: * Returns the minimum and maximum values for the dataset's range
1222: * (y-values), assuming that the series in one category are stacked.
1223: *
1224: * @param dataset the dataset (<code>null</code> not permitted).
1225: *
1226: * @return The range (<code>null</code> if the dataset contains no values).
1227: */
1228: public static Range findStackedRangeBounds(CategoryDataset dataset) {
1229: return findStackedRangeBounds(dataset, 0.0);
1230: }
1231:
1232: /**
1233: * Returns the minimum and maximum values for the dataset's range
1234: * (y-values), assuming that the series in one category are stacked.
1235: *
1236: * @param dataset the dataset (<code>null</code> not permitted).
1237: * @param base the base value for the bars.
1238: *
1239: * @return The range (<code>null</code> if the dataset contains no values).
1240: */
1241: public static Range findStackedRangeBounds(CategoryDataset dataset,
1242: double base) {
1243: if (dataset == null) {
1244: throw new IllegalArgumentException(
1245: "Null 'dataset' argument.");
1246: }
1247: Range result = null;
1248: double minimum = Double.POSITIVE_INFINITY;
1249: double maximum = Double.NEGATIVE_INFINITY;
1250: int categoryCount = dataset.getColumnCount();
1251: for (int item = 0; item < categoryCount; item++) {
1252: double positive = base;
1253: double negative = base;
1254: int seriesCount = dataset.getRowCount();
1255: for (int series = 0; series < seriesCount; series++) {
1256: Number number = dataset.getValue(series, item);
1257: if (number != null) {
1258: double value = number.doubleValue();
1259: if (value > 0.0) {
1260: positive = positive + value;
1261: }
1262: if (value < 0.0) {
1263: negative = negative + value;
1264: // '+', remember value is negative
1265: }
1266: }
1267: }
1268: minimum = Math.min(minimum, negative);
1269: maximum = Math.max(maximum, positive);
1270: }
1271: if (minimum <= maximum) {
1272: result = new Range(minimum, maximum);
1273: }
1274: return result;
1275:
1276: }
1277:
1278: /**
1279: * Returns the minimum and maximum values for the dataset's range
1280: * (y-values), assuming that the series in one category are stacked.
1281: *
1282: * @param dataset the dataset.
1283: * @param map a structure that maps series to groups.
1284: *
1285: * @return The value range (<code>null</code> if the dataset contains no
1286: * values).
1287: */
1288: public static Range findStackedRangeBounds(CategoryDataset dataset,
1289: KeyToGroupMap map) {
1290:
1291: Range result = null;
1292: if (dataset != null) {
1293:
1294: // create an array holding the group indices...
1295: int[] groupIndex = new int[dataset.getRowCount()];
1296: for (int i = 0; i < dataset.getRowCount(); i++) {
1297: groupIndex[i] = map.getGroupIndex(map.getGroup(dataset
1298: .getRowKey(i)));
1299: }
1300:
1301: // minimum and maximum for each group...
1302: int groupCount = map.getGroupCount();
1303: double[] minimum = new double[groupCount];
1304: double[] maximum = new double[groupCount];
1305:
1306: int categoryCount = dataset.getColumnCount();
1307: for (int item = 0; item < categoryCount; item++) {
1308: double[] positive = new double[groupCount];
1309: double[] negative = new double[groupCount];
1310: int seriesCount = dataset.getRowCount();
1311: for (int series = 0; series < seriesCount; series++) {
1312: Number number = dataset.getValue(series, item);
1313: if (number != null) {
1314: double value = number.doubleValue();
1315: if (value > 0.0) {
1316: positive[groupIndex[series]] = positive[groupIndex[series]]
1317: + value;
1318: }
1319: if (value < 0.0) {
1320: negative[groupIndex[series]] = negative[groupIndex[series]]
1321: + value;
1322: // '+', remember value is negative
1323: }
1324: }
1325: }
1326: for (int g = 0; g < groupCount; g++) {
1327: minimum[g] = Math.min(minimum[g], negative[g]);
1328: maximum[g] = Math.max(maximum[g], positive[g]);
1329: }
1330: }
1331: for (int j = 0; j < groupCount; j++) {
1332: result = Range.combine(result, new Range(minimum[j],
1333: maximum[j]));
1334: }
1335: }
1336: return result;
1337:
1338: }
1339:
1340: /**
1341: * Returns the minimum value in the dataset range, assuming that values in
1342: * each category are "stacked".
1343: *
1344: * @param dataset the dataset.
1345: *
1346: * @return The minimum value.
1347: */
1348: public static Number findMinimumStackedRangeValue(
1349: CategoryDataset dataset) {
1350:
1351: Number result = null;
1352: if (dataset != null) {
1353: double minimum = 0.0;
1354: int categoryCount = dataset.getRowCount();
1355: for (int item = 0; item < categoryCount; item++) {
1356: double total = 0.0;
1357:
1358: int seriesCount = dataset.getColumnCount();
1359: for (int series = 0; series < seriesCount; series++) {
1360: Number number = dataset.getValue(series, item);
1361: if (number != null) {
1362: double value = number.doubleValue();
1363: if (value < 0.0) {
1364: total = total + value;
1365: // '+', remember value is negative
1366: }
1367: }
1368: }
1369: minimum = Math.min(minimum, total);
1370:
1371: }
1372: result = new Double(minimum);
1373: }
1374: return result;
1375:
1376: }
1377:
1378: /**
1379: * Returns the maximum value in the dataset range, assuming that values in
1380: * each category are "stacked".
1381: *
1382: * @param dataset the dataset (<code>null</code> permitted).
1383: *
1384: * @return The maximum value (possibly <code>null</code>).
1385: */
1386: public static Number findMaximumStackedRangeValue(
1387: CategoryDataset dataset) {
1388:
1389: Number result = null;
1390:
1391: if (dataset != null) {
1392: double maximum = 0.0;
1393: int categoryCount = dataset.getColumnCount();
1394: for (int item = 0; item < categoryCount; item++) {
1395: double total = 0.0;
1396: int seriesCount = dataset.getRowCount();
1397: for (int series = 0; series < seriesCount; series++) {
1398: Number number = dataset.getValue(series, item);
1399: if (number != null) {
1400: double value = number.doubleValue();
1401: if (value > 0.0) {
1402: total = total + value;
1403: }
1404: }
1405: }
1406: maximum = Math.max(maximum, total);
1407: }
1408: result = new Double(maximum);
1409: }
1410:
1411: return result;
1412:
1413: }
1414:
1415: /**
1416: * Returns the minimum and maximum values for the dataset's range,
1417: * assuming that the series are stacked.
1418: *
1419: * @param dataset the dataset (<code>null</code> not permitted).
1420: *
1421: * @return The range ([0.0, 0.0] if the dataset contains no values).
1422: */
1423: public static Range findStackedRangeBounds(TableXYDataset dataset) {
1424: return findStackedRangeBounds(dataset, 0.0);
1425: }
1426:
1427: /**
1428: * Returns the minimum and maximum values for the dataset's range,
1429: * assuming that the series are stacked, using the specified base value.
1430: *
1431: * @param dataset the dataset (<code>null</code> not permitted).
1432: * @param base the base value.
1433: *
1434: * @return The range (<code>null</code> if the dataset contains no values).
1435: */
1436: public static Range findStackedRangeBounds(TableXYDataset dataset,
1437: double base) {
1438: if (dataset == null) {
1439: throw new IllegalArgumentException(
1440: "Null 'dataset' argument.");
1441: }
1442: double minimum = base;
1443: double maximum = base;
1444: for (int itemNo = 0; itemNo < dataset.getItemCount(); itemNo++) {
1445: double positive = base;
1446: double negative = base;
1447: int seriesCount = dataset.getSeriesCount();
1448: for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) {
1449: double y = dataset.getYValue(seriesNo, itemNo);
1450: if (!Double.isNaN(y)) {
1451: if (y > 0.0) {
1452: positive += y;
1453: } else {
1454: negative += y;
1455: }
1456: }
1457: }
1458: if (positive > maximum) {
1459: maximum = positive;
1460: }
1461: if (negative < minimum) {
1462: minimum = negative;
1463: }
1464: }
1465: if (minimum <= maximum) {
1466: return new Range(minimum, maximum);
1467: } else {
1468: return null;
1469: }
1470: }
1471:
1472: /**
1473: * Calculates the total for the y-values in all series for a given item
1474: * index.
1475: *
1476: * @param dataset the dataset.
1477: * @param item the item index.
1478: *
1479: * @return The total.
1480: *
1481: * @since 1.0.5
1482: */
1483: public static double calculateStackTotal(TableXYDataset dataset,
1484: int item) {
1485: double total = 0.0;
1486: int seriesCount = dataset.getSeriesCount();
1487: for (int s = 0; s < seriesCount; s++) {
1488: double value = dataset.getYValue(s, item);
1489: if (!Double.isNaN(value)) {
1490: total = total + value;
1491: }
1492: }
1493: return total;
1494: }
1495:
1496: /**
1497: * Calculates the range of values for a dataset where each item is the
1498: * running total of the items for the current series.
1499: *
1500: * @param dataset the dataset (<code>null</code> not permitted).
1501: *
1502: * @return The range.
1503: *
1504: * @see #findRangeBounds(CategoryDataset)
1505: */
1506: public static Range findCumulativeRangeBounds(
1507: CategoryDataset dataset) {
1508:
1509: if (dataset == null) {
1510: throw new IllegalArgumentException(
1511: "Null 'dataset' argument.");
1512: }
1513:
1514: boolean allItemsNull = true; // we'll set this to false if there is at
1515: // least one non-null data item...
1516: double minimum = 0.0;
1517: double maximum = 0.0;
1518: for (int row = 0; row < dataset.getRowCount(); row++) {
1519: double runningTotal = 0.0;
1520: for (int column = 0; column < dataset.getColumnCount() - 1; column++) {
1521: Number n = dataset.getValue(row, column);
1522: if (n != null) {
1523: allItemsNull = false;
1524: double value = n.doubleValue();
1525: runningTotal = runningTotal + value;
1526: minimum = Math.min(minimum, runningTotal);
1527: maximum = Math.max(maximum, runningTotal);
1528: }
1529: }
1530: }
1531: if (!allItemsNull) {
1532: return new Range(minimum, maximum);
1533: } else {
1534: return null;
1535: }
1536:
1537: }
1538:
1539: }
|