001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------------- 028 * DatasetUtilities.java 029 * --------------------- 030 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Andrzej Porebski (bug fix); 034 * Jonathan Nash (bug fix); 035 * Richard Atkinson; 036 * Andreas Schroeder (beatification) 037 * 038 * $Id: DatasetUtilities.java,v 1.18.2.5 2007/03/15 17:04:35 mungady Exp $ 039 * 040 * Changes (from 18-Sep-2001) 041 * -------------------------- 042 * 18-Sep-2001 : Added standard header and fixed DOS encoding problem (DG); 043 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); 044 * 15-Nov-2001 : Moved to package com.jrefinery.data.* in the JCommon class 045 * library (DG); 046 * Changed to handle null values from datasets (DG); 047 * Bug fix (thanks to Andrzej Porebski) - initial value now set 048 * to positive or negative infinity when iterating (DG); 049 * 22-Nov-2001 : Datasets with containing no data now return null for min and 050 * max calculations (DG); 051 * 13-Dec-2001 : Extended to handle HighLowDataset and IntervalXYDataset (DG); 052 * 15-Feb-2002 : Added getMinimumStackedRangeValue() and 053 * getMaximumStackedRangeValue() (DG); 054 * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG); 055 * 18-Mar-2002 : Fixed bug in min/max domain calculation for datasets that 056 * implement the CategoryDataset interface AND the XYDataset 057 * interface at the same time. Thanks to Jonathan Nash for the 058 * fix (DG); 059 * 23-Apr-2002 : Added getDomainExtent() and getRangeExtent() methods (DG); 060 * 13-Jun-2002 : Modified range measurements to handle 061 * IntervalCategoryDataset (DG); 062 * 12-Jul-2002 : Method name change in DomainInfo interface (DG); 063 * 30-Jul-2002 : Added pie dataset summation method (DG); 064 * 01-Oct-2002 : Added a method for constructing an XYDataset from a Function2D 065 * instance (DG); 066 * 24-Oct-2002 : Amendments required following changes to the CategoryDataset 067 * interface (DG); 068 * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG); 069 * 04-Mar-2003 : Added isEmpty(XYDataset) method (DG); 070 * 05-Mar-2003 : Added a method for creating a CategoryDataset from a 071 * KeyedValues instance (DG); 072 * 15-May-2003 : Renamed isEmpty --> isEmptyOrNull (DG); 073 * 25-Jun-2003 : Added limitPieDataset methods (RA); 074 * 26-Jun-2003 : Modified getDomainExtent() method to accept null datasets (DG); 075 * 27-Jul-2003 : Added getStackedRangeExtent(TableXYDataset data) (RA); 076 * 18-Aug-2003 : getStackedRangeExtent(TableXYDataset data) now handles null 077 * values (RA); 078 * 02-Sep-2003 : Added method to check for null or empty PieDataset (DG); 079 * 18-Sep-2003 : Fix for bug 803660 (getMaximumRangeValue for 080 * CategoryDataset) (DG); 081 * 20-Oct-2003 : Added getCumulativeRangeExtent() method (DG); 082 * 09-Jan-2003 : Added argument checking code to the createCategoryDataset() 083 * method (DG); 084 * 23-Mar-2004 : Fixed bug in getMaximumStackedRangeValue() method (DG); 085 * 31-Mar-2004 : Exposed the extent iteration algorithms to use one of them and 086 * applied noninstantiation pattern (AS); 087 * 11-May-2004 : Renamed getPieDatasetTotal --> calculatePieDatasetTotal (DG); 088 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with getYValue(); 089 * 24-Aug-2004 : Added argument checks to createCategoryDataset() method (DG); 090 * 04-Oct-2004 : Renamed ArrayUtils --> ArrayUtilities (DG); 091 * 06-Oct-2004 : Renamed findDomainExtent() --> findDomainBounds(), 092 * findRangeExtent() --> findRangeBounds() (DG); 093 * 07-Jan-2005 : Renamed findStackedRangeExtent() --> findStackedRangeBounds(), 094 * findCumulativeRangeExtent() --> findCumulativeRangeBounds(), 095 * iterateXYRangeExtent() --> iterateXYRangeBounds(), 096 * removed deprecated methods (DG); 097 * 03-Feb-2005 : The findStackedRangeBounds() methods now return null for 098 * empty datasets (DG); 099 * 03-Mar-2005 : Moved createNumberArray() and createNumberArray2D() methods 100 * from DatasetUtilities --> DataUtilities (DG); 101 * 22-Sep-2005 : Added new findStackedRangeBounds() method that takes base 102 * argument (DG); 103 * ------------- JFREECHART 1.0.x --------------------------------------------- 104 * 15-Mar-2007 : Added calculateStackTotal() method (DG); 105 * 106 */ 107 108 package org.jfree.data.general; 109 110 import java.util.ArrayList; 111 import java.util.Iterator; 112 import java.util.List; 113 114 import org.jfree.data.DomainInfo; 115 import org.jfree.data.KeyToGroupMap; 116 import org.jfree.data.KeyedValues; 117 import org.jfree.data.Range; 118 import org.jfree.data.RangeInfo; 119 import org.jfree.data.category.CategoryDataset; 120 import org.jfree.data.category.DefaultCategoryDataset; 121 import org.jfree.data.category.IntervalCategoryDataset; 122 import org.jfree.data.function.Function2D; 123 import org.jfree.data.xy.OHLCDataset; 124 import org.jfree.data.xy.IntervalXYDataset; 125 import org.jfree.data.xy.TableXYDataset; 126 import org.jfree.data.xy.XYDataset; 127 import org.jfree.data.xy.XYSeries; 128 import org.jfree.data.xy.XYSeriesCollection; 129 import org.jfree.util.ArrayUtilities; 130 131 /** 132 * A collection of useful static methods relating to datasets. 133 */ 134 public final class DatasetUtilities { 135 136 /** 137 * Private constructor for non-instanceability. 138 */ 139 private DatasetUtilities() { 140 // now try to instantiate this ;-) 141 } 142 143 /** 144 * Calculates the total of all the values in a {@link PieDataset}. If 145 * the dataset contains negative or <code>null</code> values, they are 146 * ignored. 147 * 148 * @param dataset the dataset (<code>null</code> not permitted). 149 * 150 * @return The total. 151 */ 152 public static double calculatePieDatasetTotal(PieDataset dataset) { 153 if (dataset == null) { 154 throw new IllegalArgumentException("Null 'dataset' argument."); 155 } 156 List keys = dataset.getKeys(); 157 double totalValue = 0; 158 Iterator iterator = keys.iterator(); 159 while (iterator.hasNext()) { 160 Comparable current = (Comparable) iterator.next(); 161 if (current != null) { 162 Number value = dataset.getValue(current); 163 double v = 0.0; 164 if (value != null) { 165 v = value.doubleValue(); 166 } 167 if (v > 0) { 168 totalValue = totalValue + v; 169 } 170 } 171 } 172 return totalValue; 173 } 174 175 /** 176 * Creates a pie dataset from a table dataset by taking all the values 177 * for a single row. 178 * 179 * @param dataset the dataset (<code>null</code> not permitted). 180 * @param rowKey the row key. 181 * 182 * @return A pie dataset. 183 */ 184 public static PieDataset createPieDatasetForRow(CategoryDataset dataset, 185 Comparable rowKey) { 186 int row = dataset.getRowIndex(rowKey); 187 return createPieDatasetForRow(dataset, row); 188 } 189 190 /** 191 * Creates a pie dataset from a table dataset by taking all the values 192 * for a single row. 193 * 194 * @param dataset the dataset (<code>null</code> not permitted). 195 * @param row the row (zero-based index). 196 * 197 * @return A pie dataset. 198 */ 199 public static PieDataset createPieDatasetForRow(CategoryDataset dataset, 200 int row) { 201 DefaultPieDataset result = new DefaultPieDataset(); 202 int columnCount = dataset.getColumnCount(); 203 for (int current = 0; current < columnCount; current++) { 204 Comparable columnKey = dataset.getColumnKey(current); 205 result.setValue(columnKey, dataset.getValue(row, current)); 206 } 207 return result; 208 } 209 210 /** 211 * Creates a pie dataset from a table dataset by taking all the values 212 * for a single column. 213 * 214 * @param dataset the dataset (<code>null</code> not permitted). 215 * @param columnKey the column key. 216 * 217 * @return A pie dataset. 218 */ 219 public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, 220 Comparable columnKey) { 221 int column = dataset.getColumnIndex(columnKey); 222 return createPieDatasetForColumn(dataset, column); 223 } 224 225 /** 226 * Creates a pie dataset from a {@link CategoryDataset} by taking all the 227 * values for a single column. 228 * 229 * @param dataset the dataset (<code>null</code> not permitted). 230 * @param column the column (zero-based index). 231 * 232 * @return A pie dataset. 233 */ 234 public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, 235 int column) { 236 DefaultPieDataset result = new DefaultPieDataset(); 237 int rowCount = dataset.getRowCount(); 238 for (int i = 0; i < rowCount; i++) { 239 Comparable rowKey = dataset.getRowKey(i); 240 result.setValue(rowKey, dataset.getValue(i, column)); 241 } 242 return result; 243 } 244 245 /** 246 * Creates a new pie dataset based on the supplied dataset, but modified 247 * by aggregating all the low value items (those whose value is lower 248 * than the <code>percentThreshold</code>) into a single item with the 249 * key "Other". 250 * 251 * @param source the source dataset (<code>null</code> not permitted). 252 * @param key a new key for the aggregated items (<code>null</code> not 253 * permitted). 254 * @param minimumPercent the percent threshold. 255 * 256 * @return The pie dataset with (possibly) aggregated items. 257 */ 258 public static PieDataset createConsolidatedPieDataset(PieDataset source, 259 Comparable key, 260 double minimumPercent) 261 { 262 return DatasetUtilities.createConsolidatedPieDataset( 263 source, key, minimumPercent, 2 264 ); 265 } 266 267 /** 268 * Creates a new pie dataset based on the supplied dataset, but modified 269 * by aggregating all the low value items (those whose value is lower 270 * than the <code>percentThreshold</code>) into a single item. The 271 * aggregated items are assigned the specified key. Aggregation only 272 * occurs if there are at least <code>minItems</code> items to aggregate. 273 * 274 * @param source the source dataset (<code>null</code> not permitted). 275 * @param key the key to represent the aggregated items. 276 * @param minimumPercent the percent threshold (ten percent is 0.10). 277 * @param minItems only aggregate low values if there are at least this 278 * many. 279 * 280 * @return The pie dataset with (possibly) aggregated items. 281 */ 282 public static PieDataset createConsolidatedPieDataset(PieDataset source, 283 Comparable key, 284 double minimumPercent, 285 int minItems) { 286 287 DefaultPieDataset result = new DefaultPieDataset(); 288 double total = DatasetUtilities.calculatePieDatasetTotal(source); 289 290 // Iterate and find all keys below threshold percentThreshold 291 List keys = source.getKeys(); 292 ArrayList otherKeys = new ArrayList(); 293 Iterator iterator = keys.iterator(); 294 while (iterator.hasNext()) { 295 Comparable currentKey = (Comparable) iterator.next(); 296 Number dataValue = source.getValue(currentKey); 297 if (dataValue != null) { 298 double value = dataValue.doubleValue(); 299 if (value / total < minimumPercent) { 300 otherKeys.add(currentKey); 301 } 302 } 303 } 304 305 // Create new dataset with keys above threshold percentThreshold 306 iterator = keys.iterator(); 307 double otherValue = 0; 308 while (iterator.hasNext()) { 309 Comparable currentKey = (Comparable) iterator.next(); 310 Number dataValue = source.getValue(currentKey); 311 if (dataValue != null) { 312 if (otherKeys.contains(currentKey) 313 && otherKeys.size() >= minItems) { 314 // Do not add key to dataset 315 otherValue += dataValue.doubleValue(); 316 } 317 else { 318 // Add key to dataset 319 result.setValue(currentKey, dataValue); 320 } 321 } 322 } 323 // Add other category if applicable 324 if (otherKeys.size() >= minItems) { 325 result.setValue(key, otherValue); 326 } 327 return result; 328 } 329 330 /** 331 * Creates a {@link CategoryDataset} that contains a copy of the data in an 332 * array (instances of <code>Double</code> are created to represent the 333 * data items). 334 * <p> 335 * Row and column keys are created by appending 0, 1, 2, ... to the 336 * supplied prefixes. 337 * 338 * @param rowKeyPrefix the row key prefix. 339 * @param columnKeyPrefix the column key prefix. 340 * @param data the data. 341 * 342 * @return The dataset. 343 */ 344 public static CategoryDataset createCategoryDataset(String rowKeyPrefix, 345 String columnKeyPrefix, 346 double[][] data) { 347 348 DefaultCategoryDataset result = new DefaultCategoryDataset(); 349 for (int r = 0; r < data.length; r++) { 350 String rowKey = rowKeyPrefix + (r + 1); 351 for (int c = 0; c < data[r].length; c++) { 352 String columnKey = columnKeyPrefix + (c + 1); 353 result.addValue(new Double(data[r][c]), rowKey, columnKey); 354 } 355 } 356 return result; 357 358 } 359 360 /** 361 * Creates a {@link CategoryDataset} that contains a copy of the data in 362 * an array. 363 * <p> 364 * Row and column keys are created by appending 0, 1, 2, ... to the 365 * supplied prefixes. 366 * 367 * @param rowKeyPrefix the row key prefix. 368 * @param columnKeyPrefix the column key prefix. 369 * @param data the data. 370 * 371 * @return The dataset. 372 */ 373 public static CategoryDataset createCategoryDataset(String rowKeyPrefix, 374 String columnKeyPrefix, 375 Number[][] data) { 376 377 DefaultCategoryDataset result = new DefaultCategoryDataset(); 378 for (int r = 0; r < data.length; r++) { 379 String rowKey = rowKeyPrefix + (r + 1); 380 for (int c = 0; c < data[r].length; c++) { 381 String columnKey = columnKeyPrefix + (c + 1); 382 result.addValue(data[r][c], rowKey, columnKey); 383 } 384 } 385 return result; 386 387 } 388 389 /** 390 * Creates a {@link CategoryDataset} that contains a copy of the data in 391 * an array (instances of <code>Double</code> are created to represent the 392 * data items). 393 * <p> 394 * Row and column keys are taken from the supplied arrays. 395 * 396 * @param rowKeys the row keys (<code>null</code> not permitted). 397 * @param columnKeys the column keys (<code>null</code> not permitted). 398 * @param data the data. 399 * 400 * @return The dataset. 401 */ 402 public static CategoryDataset createCategoryDataset(Comparable[] rowKeys, 403 Comparable[] columnKeys, 404 double[][] data) { 405 406 // check arguments... 407 if (rowKeys == null) { 408 throw new IllegalArgumentException("Null 'rowKeys' argument."); 409 } 410 if (columnKeys == null) { 411 throw new IllegalArgumentException("Null 'columnKeys' argument."); 412 } 413 if (ArrayUtilities.hasDuplicateItems(rowKeys)) { 414 throw new IllegalArgumentException("Duplicate items in 'rowKeys'."); 415 } 416 if (ArrayUtilities.hasDuplicateItems(columnKeys)) { 417 throw new IllegalArgumentException( 418 "Duplicate items in 'columnKeys'." 419 ); 420 } 421 if (rowKeys.length != data.length) { 422 throw new IllegalArgumentException( 423 "The number of row keys does not match the number of rows in " 424 + "the data array." 425 ); 426 } 427 int columnCount = 0; 428 for (int r = 0; r < data.length; r++) { 429 columnCount = Math.max(columnCount, data[r].length); 430 } 431 if (columnKeys.length != columnCount) { 432 throw new IllegalArgumentException( 433 "The number of column keys does not match the number of " 434 + "columns in the data array." 435 ); 436 } 437 438 // now do the work... 439 DefaultCategoryDataset result = new DefaultCategoryDataset(); 440 for (int r = 0; r < data.length; r++) { 441 Comparable rowKey = rowKeys[r]; 442 for (int c = 0; c < data[r].length; c++) { 443 Comparable columnKey = columnKeys[c]; 444 result.addValue(new Double(data[r][c]), rowKey, columnKey); 445 } 446 } 447 return result; 448 449 } 450 451 /** 452 * Creates a {@link CategoryDataset} by copying the data from the supplied 453 * {@link KeyedValues} instance. 454 * 455 * @param rowKey the row key (<code>null</code> not permitted). 456 * @param rowData the row data (<code>null</code> not permitted). 457 * 458 * @return A dataset. 459 */ 460 public static CategoryDataset createCategoryDataset(Comparable rowKey, 461 KeyedValues rowData) { 462 463 if (rowKey == null) { 464 throw new IllegalArgumentException("Null 'rowKey' argument."); 465 } 466 if (rowData == null) { 467 throw new IllegalArgumentException("Null 'rowData' argument."); 468 } 469 DefaultCategoryDataset result = new DefaultCategoryDataset(); 470 for (int i = 0; i < rowData.getItemCount(); i++) { 471 result.addValue(rowData.getValue(i), rowKey, rowData.getKey(i)); 472 } 473 return result; 474 475 } 476 477 /** 478 * Creates an {@link XYDataset} by sampling the specified function over a 479 * fixed range. 480 * 481 * @param f the function (<code>null</code> not permitted). 482 * @param start the start value for the range. 483 * @param end the end value for the range. 484 * @param samples the number of sample points (must be > 1). 485 * @param seriesKey the key to give the resulting series 486 * (<code>null</code> not permitted). 487 * 488 * @return A dataset. 489 */ 490 public static XYDataset sampleFunction2D(Function2D f, 491 double start, 492 double end, 493 int samples, 494 Comparable seriesKey) { 495 496 if (f == null) { 497 throw new IllegalArgumentException("Null 'f' argument."); 498 } 499 if (seriesKey == null) { 500 throw new IllegalArgumentException("Null 'seriesKey' argument."); 501 } 502 if (start >= end) { 503 throw new IllegalArgumentException("Requires 'start' < 'end'."); 504 } 505 if (samples < 2) { 506 throw new IllegalArgumentException("Requires 'samples' > 1"); 507 } 508 509 XYSeries series = new XYSeries(seriesKey); 510 double step = (end - start) / samples; 511 for (int i = 0; i <= samples; i++) { 512 double x = start + (step * i); 513 series.add(x, f.getValue(x)); 514 } 515 XYSeriesCollection collection = new XYSeriesCollection(series); 516 return collection; 517 518 } 519 520 /** 521 * Returns <code>true</code> if the dataset is empty (or <code>null</code>), 522 * and <code>false</code> otherwise. 523 * 524 * @param dataset the dataset (<code>null</code> permitted). 525 * 526 * @return A boolean. 527 */ 528 public static boolean isEmptyOrNull(PieDataset dataset) { 529 530 if (dataset == null) { 531 return true; 532 } 533 534 int itemCount = dataset.getItemCount(); 535 if (itemCount == 0) { 536 return true; 537 } 538 539 for (int item = 0; item < itemCount; item++) { 540 Number y = dataset.getValue(item); 541 if (y != null) { 542 double yy = y.doubleValue(); 543 if (yy > 0.0) { 544 return false; 545 } 546 } 547 } 548 549 return true; 550 551 } 552 553 /** 554 * Returns <code>true</code> if the dataset is empty (or <code>null</code>), 555 * and <code>false</code> otherwise. 556 * 557 * @param dataset the dataset (<code>null</code> permitted). 558 * 559 * @return A boolean. 560 */ 561 public static boolean isEmptyOrNull(CategoryDataset dataset) { 562 563 if (dataset == null) { 564 return true; 565 } 566 567 int rowCount = dataset.getRowCount(); 568 int columnCount = dataset.getColumnCount(); 569 if (rowCount == 0 || columnCount == 0) { 570 return true; 571 } 572 573 for (int r = 0; r < rowCount; r++) { 574 for (int c = 0; c < columnCount; c++) { 575 if (dataset.getValue(r, c) != null) { 576 return false; 577 } 578 579 } 580 } 581 582 return true; 583 584 } 585 586 /** 587 * Returns <code>true</code> if the dataset is empty (or <code>null</code>), 588 * and <code>false</code> otherwise. 589 * 590 * @param dataset the dataset (<code>null</code> permitted). 591 * 592 * @return A boolean. 593 */ 594 public static boolean isEmptyOrNull(XYDataset dataset) { 595 596 boolean result = true; 597 598 if (dataset != null) { 599 for (int s = 0; s < dataset.getSeriesCount(); s++) { 600 if (dataset.getItemCount(s) > 0) { 601 result = false; 602 continue; 603 } 604 } 605 } 606 607 return result; 608 609 } 610 611 /** 612 * Returns the range of values in the domain (x-values) of a dataset. 613 * 614 * @param dataset the dataset (<code>null</code> not permitted). 615 * 616 * @return The range of values (possibly <code>null</code>). 617 */ 618 public static Range findDomainBounds(XYDataset dataset) { 619 return findDomainBounds(dataset, true); 620 } 621 622 /** 623 * Returns the range of values in the domain (x-values) of a dataset. 624 * 625 * @param dataset the dataset (<code>null</code> not permitted). 626 * @param includeInterval determines whether or not the x-interval is taken 627 * into account (only applies if the dataset is an 628 * {@link IntervalXYDataset}). 629 * 630 * @return The range of values (possibly <code>null</code>). 631 */ 632 public static Range findDomainBounds(XYDataset dataset, 633 boolean includeInterval) { 634 635 if (dataset == null) { 636 throw new IllegalArgumentException("Null 'dataset' argument."); 637 } 638 639 Range result = null; 640 // if the dataset implements DomainInfo, life is easier 641 if (dataset instanceof DomainInfo) { 642 DomainInfo info = (DomainInfo) dataset; 643 result = info.getDomainBounds(includeInterval); 644 } 645 else { 646 result = iterateDomainBounds(dataset, includeInterval); 647 } 648 return result; 649 650 } 651 652 /** 653 * Iterates over the items in an {@link XYDataset} to find 654 * the range of x-values. 655 * 656 * @param dataset the dataset (<code>null</code> not permitted). 657 * 658 * @return The range (possibly <code>null</code>). 659 */ 660 public static Range iterateDomainBounds(XYDataset dataset) { 661 return iterateDomainBounds(dataset, true); 662 } 663 664 /** 665 * Iterates over the items in an {@link XYDataset} to find 666 * the range of x-values. 667 * 668 * @param dataset the dataset (<code>null</code> not permitted). 669 * @param includeInterval a flag that determines, for an IntervalXYDataset, 670 * whether the x-interval or just the x-value is 671 * used to determine the overall range. 672 * 673 * @return The range (possibly <code>null</code>). 674 */ 675 public static Range iterateDomainBounds(XYDataset dataset, 676 boolean includeInterval) { 677 if (dataset == null) { 678 throw new IllegalArgumentException("Null 'dataset' argument."); 679 } 680 double minimum = Double.POSITIVE_INFINITY; 681 double maximum = Double.NEGATIVE_INFINITY; 682 int seriesCount = dataset.getSeriesCount(); 683 double lvalue; 684 double uvalue; 685 if (includeInterval && dataset instanceof IntervalXYDataset) { 686 IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; 687 for (int series = 0; series < seriesCount; series++) { 688 int itemCount = dataset.getItemCount(series); 689 for (int item = 0; item < itemCount; item++) { 690 lvalue = intervalXYData.getStartXValue(series, item); 691 uvalue = intervalXYData.getEndXValue(series, item); 692 minimum = Math.min(minimum, lvalue); 693 maximum = Math.max(maximum, uvalue); 694 } 695 } 696 } 697 else { 698 for (int series = 0; series < seriesCount; series++) { 699 int itemCount = dataset.getItemCount(series); 700 for (int item = 0; item < itemCount; item++) { 701 lvalue = dataset.getXValue(series, item); 702 uvalue = lvalue; 703 minimum = Math.min(minimum, lvalue); 704 maximum = Math.max(maximum, uvalue); 705 } 706 } 707 } 708 if (minimum > maximum) { 709 return null; 710 } 711 else { 712 return new Range(minimum, maximum); 713 } 714 } 715 716 /** 717 * Returns the range of values in the range for the dataset. This method 718 * is the partner for the getDomainExtent method. 719 * 720 * @param dataset the dataset (<code>null</code> not permitted). 721 * 722 * @return The range (possibly <code>null</code>). 723 */ 724 public static Range findRangeBounds(CategoryDataset dataset) { 725 return findRangeBounds(dataset, true); 726 } 727 728 /** 729 * Returns the range of values in the range for the dataset. This method 730 * is the partner for the getDomainExtent method. 731 * 732 * @param dataset the dataset (<code>null</code> not permitted). 733 * @param includeInterval a flag that determines whether or not the 734 * y-interval is taken into account. 735 * 736 * @return The range (possibly <code>null</code>). 737 */ 738 public static Range findRangeBounds(CategoryDataset dataset, 739 boolean includeInterval) { 740 if (dataset == null) { 741 throw new IllegalArgumentException("Null 'dataset' argument."); 742 } 743 Range result = null; 744 if (dataset instanceof RangeInfo) { 745 RangeInfo info = (RangeInfo) dataset; 746 result = info.getRangeBounds(includeInterval); 747 } 748 else { 749 result = iterateCategoryRangeBounds(dataset, includeInterval); 750 } 751 return result; 752 } 753 754 /** 755 * Returns the range of values in the range for the dataset. This method 756 * is the partner for the {@link #findDomainBounds(XYDataset)} method. 757 * 758 * @param dataset the dataset (<code>null</code> not permitted). 759 * 760 * @return The range (possibly <code>null</code>). 761 */ 762 public static Range findRangeBounds(XYDataset dataset) { 763 return findRangeBounds(dataset, true); 764 } 765 766 /** 767 * Returns the range of values in the range for the dataset. This method 768 * is the partner for the {@link #findDomainBounds(XYDataset)} method. 769 * 770 * @param dataset the dataset (<code>null</code> not permitted). 771 * @param includeInterval a flag that determines whether or not the 772 * y-interval is taken into account. 773 * 774 * 775 * @return The range (possibly <code>null</code>). 776 */ 777 public static Range findRangeBounds(XYDataset dataset, 778 boolean includeInterval) { 779 if (dataset == null) { 780 throw new IllegalArgumentException("Null 'dataset' argument."); 781 } 782 Range result = null; 783 if (dataset instanceof RangeInfo) { 784 RangeInfo info = (RangeInfo) dataset; 785 result = info.getRangeBounds(includeInterval); 786 } 787 else { 788 result = iterateXYRangeBounds(dataset); 789 } 790 return result; 791 } 792 793 /** 794 * Iterates over the data item of the category dataset to find 795 * the range bounds. 796 * 797 * @param dataset the dataset (<code>null</code> not permitted). 798 * @param includeInterval a flag that determines whether or not the 799 * y-interval is taken into account. 800 * 801 * @return The range (possibly <code>null</code>). 802 */ 803 public static Range iterateCategoryRangeBounds(CategoryDataset dataset, 804 boolean includeInterval) { 805 double minimum = Double.POSITIVE_INFINITY; 806 double maximum = Double.NEGATIVE_INFINITY; 807 boolean interval = includeInterval 808 && dataset instanceof IntervalCategoryDataset; 809 int rowCount = dataset.getRowCount(); 810 int columnCount = dataset.getColumnCount(); 811 for (int row = 0; row < rowCount; row++) { 812 for (int column = 0; column < columnCount; column++) { 813 Number lvalue; 814 Number uvalue; 815 if (interval) { 816 IntervalCategoryDataset icd 817 = (IntervalCategoryDataset) dataset; 818 lvalue = icd.getStartValue(row, column); 819 uvalue = icd.getEndValue(row, column); 820 } 821 else { 822 lvalue = dataset.getValue(row, column); 823 uvalue = lvalue; 824 } 825 if (lvalue != null) { 826 minimum = Math.min(minimum, lvalue.doubleValue()); 827 } 828 if (uvalue != null) { 829 maximum = Math.max(maximum, uvalue.doubleValue()); 830 } 831 } 832 } 833 if (minimum == Double.POSITIVE_INFINITY) { 834 return null; 835 } 836 else { 837 return new Range(minimum, maximum); 838 } 839 } 840 841 /** 842 * Iterates over the data item of the xy dataset to find 843 * the range bounds. 844 * 845 * @param dataset the dataset (<code>null</code> not permitted). 846 * 847 * @return The range (possibly <code>null</code>). 848 */ 849 public static Range iterateXYRangeBounds(XYDataset dataset) { 850 double minimum = Double.POSITIVE_INFINITY; 851 double maximum = Double.NEGATIVE_INFINITY; 852 int seriesCount = dataset.getSeriesCount(); 853 for (int series = 0; series < seriesCount; series++) { 854 int itemCount = dataset.getItemCount(series); 855 for (int item = 0; item < itemCount; item++) { 856 double lvalue; 857 double uvalue; 858 if (dataset instanceof IntervalXYDataset) { 859 IntervalXYDataset intervalXYData 860 = (IntervalXYDataset) dataset; 861 lvalue = intervalXYData.getStartYValue(series, item); 862 uvalue = intervalXYData.getEndYValue(series, item); 863 } 864 else if (dataset instanceof OHLCDataset) { 865 OHLCDataset highLowData = (OHLCDataset) dataset; 866 lvalue = highLowData.getLowValue(series, item); 867 uvalue = highLowData.getHighValue(series, item); 868 } 869 else { 870 lvalue = dataset.getYValue(series, item); 871 uvalue = lvalue; 872 } 873 if (!Double.isNaN(lvalue)) { 874 minimum = Math.min(minimum, lvalue); 875 } 876 if (!Double.isNaN(uvalue)) { 877 maximum = Math.max(maximum, uvalue); 878 } 879 } 880 } 881 if (minimum == Double.POSITIVE_INFINITY) { 882 return null; 883 } 884 else { 885 return new Range(minimum, maximum); 886 } 887 } 888 889 /** 890 * Finds the minimum domain (or X) value for the specified dataset. This 891 * is easy if the dataset implements the {@link DomainInfo} interface (a 892 * good idea if there is an efficient way to determine the minimum value). 893 * Otherwise, it involves iterating over the entire data-set. 894 * <p> 895 * Returns <code>null</code> if all the data values in the dataset are 896 * <code>null</code>. 897 * 898 * @param dataset the dataset (<code>null</code> not permitted). 899 * 900 * @return The minimum value (possibly <code>null</code>). 901 */ 902 public static Number findMinimumDomainValue(XYDataset dataset) { 903 if (dataset == null) { 904 throw new IllegalArgumentException("Null 'dataset' argument."); 905 } 906 Number result = null; 907 // if the dataset implements DomainInfo, life is easy 908 if (dataset instanceof DomainInfo) { 909 DomainInfo info = (DomainInfo) dataset; 910 return new Double(info.getDomainLowerBound(true)); 911 } 912 else { 913 double minimum = Double.POSITIVE_INFINITY; 914 int seriesCount = dataset.getSeriesCount(); 915 for (int series = 0; series < seriesCount; series++) { 916 int itemCount = dataset.getItemCount(series); 917 for (int item = 0; item < itemCount; item++) { 918 919 double value; 920 if (dataset instanceof IntervalXYDataset) { 921 IntervalXYDataset intervalXYData 922 = (IntervalXYDataset) dataset; 923 value = intervalXYData.getStartXValue(series, item); 924 } 925 else { 926 value = dataset.getXValue(series, item); 927 } 928 if (!Double.isNaN(value)) { 929 minimum = Math.min(minimum, value); 930 } 931 932 } 933 } 934 if (minimum == Double.POSITIVE_INFINITY) { 935 result = null; 936 } 937 else { 938 result = new Double(minimum); 939 } 940 } 941 942 return result; 943 } 944 945 /** 946 * Returns the maximum domain value for the specified dataset. This is 947 * easy if the dataset implements the {@link DomainInfo} interface (a good 948 * idea if there is an efficient way to determine the maximum value). 949 * Otherwise, it involves iterating over the entire data-set. Returns 950 * <code>null</code> if all the data values in the dataset are 951 * <code>null</code>. 952 * 953 * @param dataset the dataset (<code>null</code> not permitted). 954 * 955 * @return The maximum value (possibly <code>null</code>). 956 */ 957 public static Number findMaximumDomainValue(XYDataset dataset) { 958 if (dataset == null) { 959 throw new IllegalArgumentException("Null 'dataset' argument."); 960 } 961 Number result = null; 962 // if the dataset implements DomainInfo, life is easy 963 if (dataset instanceof DomainInfo) { 964 DomainInfo info = (DomainInfo) dataset; 965 return new Double(info.getDomainUpperBound(true)); 966 } 967 968 // hasn't implemented DomainInfo, so iterate... 969 else { 970 double maximum = Double.NEGATIVE_INFINITY; 971 int seriesCount = dataset.getSeriesCount(); 972 for (int series = 0; series < seriesCount; series++) { 973 int itemCount = dataset.getItemCount(series); 974 for (int item = 0; item < itemCount; item++) { 975 976 double value; 977 if (dataset instanceof IntervalXYDataset) { 978 IntervalXYDataset intervalXYData 979 = (IntervalXYDataset) dataset; 980 value = intervalXYData.getEndXValue(series, item); 981 } 982 else { 983 value = dataset.getXValue(series, item); 984 } 985 if (!Double.isNaN(value)) { 986 maximum = Math.max(maximum, value); 987 } 988 } 989 } 990 if (maximum == Double.NEGATIVE_INFINITY) { 991 result = null; 992 } 993 else { 994 result = new Double(maximum); 995 } 996 997 } 998 999 return result; 1000 } 1001 1002 /** 1003 * Returns the minimum range value for the specified dataset. This is 1004 * easy if the dataset implements the {@link RangeInfo} interface (a good 1005 * idea if there is an efficient way to determine the minimum value). 1006 * Otherwise, it involves iterating over the entire data-set. Returns 1007 * <code>null</code> if all the data values in the dataset are 1008 * <code>null</code>. 1009 * 1010 * @param dataset the dataset (<code>null</code> not permitted). 1011 * 1012 * @return The minimum value (possibly <code>null</code>). 1013 */ 1014 public static Number findMinimumRangeValue(CategoryDataset dataset) { 1015 1016 // check parameters... 1017 if (dataset == null) { 1018 throw new IllegalArgumentException("Null 'dataset' argument."); 1019 } 1020 1021 // work out the minimum value... 1022 if (dataset instanceof RangeInfo) { 1023 RangeInfo info = (RangeInfo) dataset; 1024 return new Double(info.getRangeLowerBound(true)); 1025 } 1026 1027 // hasn't implemented RangeInfo, so we'll have to iterate... 1028 else { 1029 double minimum = Double.POSITIVE_INFINITY; 1030 int seriesCount = dataset.getRowCount(); 1031 int itemCount = dataset.getColumnCount(); 1032 for (int series = 0; series < seriesCount; series++) { 1033 for (int item = 0; item < itemCount; item++) { 1034 Number value; 1035 if (dataset instanceof IntervalCategoryDataset) { 1036 IntervalCategoryDataset icd 1037 = (IntervalCategoryDataset) dataset; 1038 value = icd.getStartValue(series, item); 1039 } 1040 else { 1041 value = dataset.getValue(series, item); 1042 } 1043 if (value != null) { 1044 minimum = Math.min(minimum, value.doubleValue()); 1045 } 1046 } 1047 } 1048 if (minimum == Double.POSITIVE_INFINITY) { 1049 return null; 1050 } 1051 else { 1052 return new Double(minimum); 1053 } 1054 1055 } 1056 1057 } 1058 1059 /** 1060 * Returns the minimum range value for the specified dataset. This is 1061 * easy if the dataset implements the {@link RangeInfo} interface (a good 1062 * idea if there is an efficient way to determine the minimum value). 1063 * Otherwise, it involves iterating over the entire data-set. Returns 1064 * <code>null</code> if all the data values in the dataset are 1065 * <code>null</code>. 1066 * 1067 * @param dataset the dataset (<code>null</code> not permitted). 1068 * 1069 * @return The minimum value (possibly <code>null</code>). 1070 */ 1071 public static Number findMinimumRangeValue(XYDataset dataset) { 1072 1073 if (dataset == null) { 1074 throw new IllegalArgumentException("Null 'dataset' argument."); 1075 } 1076 1077 // work out the minimum value... 1078 if (dataset instanceof RangeInfo) { 1079 RangeInfo info = (RangeInfo) dataset; 1080 return new Double(info.getRangeLowerBound(true)); 1081 } 1082 1083 // hasn't implemented RangeInfo, so we'll have to iterate... 1084 else { 1085 double minimum = Double.POSITIVE_INFINITY; 1086 int seriesCount = dataset.getSeriesCount(); 1087 for (int series = 0; series < seriesCount; series++) { 1088 int itemCount = dataset.getItemCount(series); 1089 for (int item = 0; item < itemCount; item++) { 1090 1091 double value; 1092 if (dataset instanceof IntervalXYDataset) { 1093 IntervalXYDataset intervalXYData 1094 = (IntervalXYDataset) dataset; 1095 value = intervalXYData.getStartYValue(series, item); 1096 } 1097 else if (dataset instanceof OHLCDataset) { 1098 OHLCDataset highLowData = (OHLCDataset) dataset; 1099 value = highLowData.getLowValue(series, item); 1100 } 1101 else { 1102 value = dataset.getYValue(series, item); 1103 } 1104 if (!Double.isNaN(value)) { 1105 minimum = Math.min(minimum, value); 1106 } 1107 1108 } 1109 } 1110 if (minimum == Double.POSITIVE_INFINITY) { 1111 return null; 1112 } 1113 else { 1114 return new Double(minimum); 1115 } 1116 1117 } 1118 1119 } 1120 1121 /** 1122 * Returns the maximum range value for the specified dataset. This is easy 1123 * if the dataset implements the {@link RangeInfo} interface (a good idea 1124 * if there is an efficient way to determine the maximum value). 1125 * Otherwise, it involves iterating over the entire data-set. Returns 1126 * <code>null</code> if all the data values are <code>null</code>. 1127 * 1128 * @param dataset the dataset (<code>null</code> not permitted). 1129 * 1130 * @return The maximum value (possibly <code>null</code>). 1131 */ 1132 public static Number findMaximumRangeValue(CategoryDataset dataset) { 1133 1134 if (dataset == null) { 1135 throw new IllegalArgumentException("Null 'dataset' argument."); 1136 } 1137 1138 // work out the minimum value... 1139 if (dataset instanceof RangeInfo) { 1140 RangeInfo info = (RangeInfo) dataset; 1141 return new Double(info.getRangeUpperBound(true)); 1142 } 1143 1144 // hasn't implemented RangeInfo, so we'll have to iterate... 1145 else { 1146 1147 double maximum = Double.NEGATIVE_INFINITY; 1148 int seriesCount = dataset.getRowCount(); 1149 int itemCount = dataset.getColumnCount(); 1150 for (int series = 0; series < seriesCount; series++) { 1151 for (int item = 0; item < itemCount; item++) { 1152 Number value; 1153 if (dataset instanceof IntervalCategoryDataset) { 1154 IntervalCategoryDataset icd 1155 = (IntervalCategoryDataset) dataset; 1156 value = icd.getEndValue(series, item); 1157 } 1158 else { 1159 value = dataset.getValue(series, item); 1160 } 1161 if (value != null) { 1162 maximum = Math.max(maximum, value.doubleValue()); 1163 } 1164 } 1165 } 1166 if (maximum == Double.NEGATIVE_INFINITY) { 1167 return null; 1168 } 1169 else { 1170 return new Double(maximum); 1171 } 1172 1173 } 1174 1175 } 1176 1177 /** 1178 * Returns the maximum range value for the specified dataset. This is 1179 * easy if the dataset implements the {@link RangeInfo} interface (a good 1180 * idea if there is an efficient way to determine the maximum value). 1181 * Otherwise, it involves iterating over the entire data-set. Returns 1182 * <code>null</code> if all the data values are <code>null</code>. 1183 * 1184 * @param dataset the dataset (<code>null</code> not permitted). 1185 * 1186 * @return The maximum value (possibly <code>null</code>). 1187 */ 1188 public static Number findMaximumRangeValue(XYDataset dataset) { 1189 1190 if (dataset == null) { 1191 throw new IllegalArgumentException("Null 'dataset' argument."); 1192 } 1193 1194 // work out the minimum value... 1195 if (dataset instanceof RangeInfo) { 1196 RangeInfo info = (RangeInfo) dataset; 1197 return new Double(info.getRangeUpperBound(true)); 1198 } 1199 1200 // hasn't implemented RangeInfo, so we'll have to iterate... 1201 else { 1202 1203 double maximum = Double.NEGATIVE_INFINITY; 1204 int seriesCount = dataset.getSeriesCount(); 1205 for (int series = 0; series < seriesCount; series++) { 1206 int itemCount = dataset.getItemCount(series); 1207 for (int item = 0; item < itemCount; item++) { 1208 double value; 1209 if (dataset instanceof IntervalXYDataset) { 1210 IntervalXYDataset intervalXYData 1211 = (IntervalXYDataset) dataset; 1212 value = intervalXYData.getEndYValue(series, item); 1213 } 1214 else if (dataset instanceof OHLCDataset) { 1215 OHLCDataset highLowData = (OHLCDataset) dataset; 1216 value = highLowData.getHighValue(series, item); 1217 } 1218 else { 1219 value = dataset.getYValue(series, item); 1220 } 1221 if (!Double.isNaN(value)) { 1222 maximum = Math.max(maximum, value); 1223 } 1224 } 1225 } 1226 if (maximum == Double.NEGATIVE_INFINITY) { 1227 return null; 1228 } 1229 else { 1230 return new Double(maximum); 1231 } 1232 1233 } 1234 1235 } 1236 1237 /** 1238 * Returns the minimum and maximum values for the dataset's range 1239 * (y-values), assuming that the series in one category are stacked. 1240 * 1241 * @param dataset the dataset (<code>null</code> not permitted). 1242 * 1243 * @return The range (<code>null</code> if the dataset contains no values). 1244 */ 1245 public static Range findStackedRangeBounds(CategoryDataset dataset) { 1246 return findStackedRangeBounds(dataset, 0.0); 1247 } 1248 1249 /** 1250 * Returns the minimum and maximum values for the dataset's range 1251 * (y-values), assuming that the series in one category are stacked. 1252 * 1253 * @param dataset the dataset (<code>null</code> not permitted). 1254 * @param base the base value for the bars. 1255 * 1256 * @return The range (<code>null</code> if the dataset contains no values). 1257 */ 1258 public static Range findStackedRangeBounds(CategoryDataset dataset, 1259 double base) { 1260 if (dataset == null) { 1261 throw new IllegalArgumentException("Null 'dataset' argument."); 1262 } 1263 Range result = null; 1264 double minimum = Double.POSITIVE_INFINITY; 1265 double maximum = Double.NEGATIVE_INFINITY; 1266 int categoryCount = dataset.getColumnCount(); 1267 for (int item = 0; item < categoryCount; item++) { 1268 double positive = base; 1269 double negative = base; 1270 int seriesCount = dataset.getRowCount(); 1271 for (int series = 0; series < seriesCount; series++) { 1272 Number number = dataset.getValue(series, item); 1273 if (number != null) { 1274 double value = number.doubleValue(); 1275 if (value > 0.0) { 1276 positive = positive + value; 1277 } 1278 if (value < 0.0) { 1279 negative = negative + value; 1280 // '+', remember value is negative 1281 } 1282 } 1283 } 1284 minimum = Math.min(minimum, negative); 1285 maximum = Math.max(maximum, positive); 1286 } 1287 if (minimum <= maximum) { 1288 result = new Range(minimum, maximum); 1289 } 1290 return result; 1291 1292 } 1293 1294 /** 1295 * Returns the minimum and maximum values for the dataset's range 1296 * (y-values), assuming that the series in one category are stacked. 1297 * 1298 * @param dataset the dataset. 1299 * @param map a structure that maps series to groups. 1300 * 1301 * @return The value range (<code>null</code> if the dataset contains no 1302 * values). 1303 */ 1304 public static Range findStackedRangeBounds(CategoryDataset dataset, 1305 KeyToGroupMap map) { 1306 1307 Range result = null; 1308 if (dataset != null) { 1309 1310 // create an array holding the group indices... 1311 int[] groupIndex = new int[dataset.getRowCount()]; 1312 for (int i = 0; i < dataset.getRowCount(); i++) { 1313 groupIndex[i] = map.getGroupIndex( 1314 map.getGroup(dataset.getRowKey(i)) 1315 ); 1316 } 1317 1318 // minimum and maximum for each group... 1319 int groupCount = map.getGroupCount(); 1320 double[] minimum = new double[groupCount]; 1321 double[] maximum = new double[groupCount]; 1322 1323 int categoryCount = dataset.getColumnCount(); 1324 for (int item = 0; item < categoryCount; item++) { 1325 double[] positive = new double[groupCount]; 1326 double[] negative = new double[groupCount]; 1327 int seriesCount = dataset.getRowCount(); 1328 for (int series = 0; series < seriesCount; series++) { 1329 Number number = dataset.getValue(series, item); 1330 if (number != null) { 1331 double value = number.doubleValue(); 1332 if (value > 0.0) { 1333 positive[groupIndex[series]] 1334 = positive[groupIndex[series]] + value; 1335 } 1336 if (value < 0.0) { 1337 negative[groupIndex[series]] 1338 = negative[groupIndex[series]] + value; 1339 // '+', remember value is negative 1340 } 1341 } 1342 } 1343 for (int g = 0; g < groupCount; g++) { 1344 minimum[g] = Math.min(minimum[g], negative[g]); 1345 maximum[g] = Math.max(maximum[g], positive[g]); 1346 } 1347 } 1348 for (int j = 0; j < groupCount; j++) { 1349 result = Range.combine( 1350 result, new Range(minimum[j], maximum[j]) 1351 ); 1352 } 1353 } 1354 return result; 1355 1356 } 1357 1358 /** 1359 * Returns the minimum value in the dataset range, assuming that values in 1360 * each category are "stacked". 1361 * 1362 * @param dataset the dataset. 1363 * 1364 * @return The minimum value. 1365 */ 1366 public static Number findMinimumStackedRangeValue(CategoryDataset dataset) { 1367 1368 Number result = null; 1369 if (dataset != null) { 1370 double minimum = 0.0; 1371 int categoryCount = dataset.getRowCount(); 1372 for (int item = 0; item < categoryCount; item++) { 1373 double total = 0.0; 1374 1375 int seriesCount = dataset.getColumnCount(); 1376 for (int series = 0; series < seriesCount; series++) { 1377 Number number = dataset.getValue(series, item); 1378 if (number != null) { 1379 double value = number.doubleValue(); 1380 if (value < 0.0) { 1381 total = total + value; 1382 // '+', remember value is negative 1383 } 1384 } 1385 } 1386 minimum = Math.min(minimum, total); 1387 1388 } 1389 result = new Double(minimum); 1390 } 1391 return result; 1392 1393 } 1394 1395 /** 1396 * Returns the maximum value in the dataset range, assuming that values in 1397 * each category are "stacked". 1398 * 1399 * @param dataset the dataset (<code>null</code> permitted). 1400 * 1401 * @return The maximum value (possibly <code>null</code>). 1402 */ 1403 public static Number findMaximumStackedRangeValue(CategoryDataset dataset) { 1404 1405 Number result = null; 1406 1407 if (dataset != null) { 1408 double maximum = 0.0; 1409 int categoryCount = dataset.getColumnCount(); 1410 for (int item = 0; item < categoryCount; item++) { 1411 double total = 0.0; 1412 int seriesCount = dataset.getRowCount(); 1413 for (int series = 0; series < seriesCount; series++) { 1414 Number number = dataset.getValue(series, item); 1415 if (number != null) { 1416 double value = number.doubleValue(); 1417 if (value > 0.0) { 1418 total = total + value; 1419 } 1420 } 1421 } 1422 maximum = Math.max(maximum, total); 1423 } 1424 result = new Double(maximum); 1425 } 1426 1427 return result; 1428 1429 } 1430 1431 /** 1432 * Returns the minimum and maximum values for the dataset's range, 1433 * assuming that the series are stacked. 1434 * 1435 * @param dataset the dataset (<code>null</code> not permitted). 1436 * 1437 * @return The range ([0.0, 0.0] if the dataset contains no values). 1438 */ 1439 public static Range findStackedRangeBounds(TableXYDataset dataset) { 1440 return findStackedRangeBounds(dataset, 0.0); 1441 } 1442 1443 /** 1444 * Returns the minimum and maximum values for the dataset's range, 1445 * assuming that the series are stacked, using the specified base value. 1446 * 1447 * @param dataset the dataset (<code>null</code> not permitted). 1448 * @param base the base value. 1449 * 1450 * @return The range (<code>null</code> if the dataset contains no values). 1451 */ 1452 public static Range findStackedRangeBounds(TableXYDataset dataset, 1453 double base) { 1454 if (dataset == null) { 1455 throw new IllegalArgumentException("Null 'dataset' argument."); 1456 } 1457 double minimum = base; 1458 double maximum = base; 1459 for (int itemNo = 0; itemNo < dataset.getItemCount(); itemNo++) { 1460 double positive = base; 1461 double negative = base; 1462 int seriesCount = dataset.getSeriesCount(); 1463 for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) { 1464 double y = dataset.getYValue(seriesNo, itemNo); 1465 if (!Double.isNaN(y)) { 1466 if (y > 0.0) { 1467 positive += y; 1468 } 1469 else { 1470 negative += y; 1471 } 1472 } 1473 } 1474 if (positive > maximum) { 1475 maximum = positive; 1476 } 1477 if (negative < minimum) { 1478 minimum = negative; 1479 } 1480 } 1481 if (minimum <= maximum) { 1482 return new Range(minimum, maximum); 1483 } 1484 else { 1485 return null; 1486 } 1487 } 1488 1489 /** 1490 * Calculates the total for the y-values in all series for a given item 1491 * index. 1492 * 1493 * @param dataset the dataset. 1494 * @param item the item index. 1495 * 1496 * @return The total. 1497 * 1498 * @since 1.0.5 1499 */ 1500 public static double calculateStackTotal(TableXYDataset dataset, int item) { 1501 double total = 0.0; 1502 int seriesCount = dataset.getSeriesCount(); 1503 for (int s = 0; s < seriesCount; s++) { 1504 double value = dataset.getYValue(s, item); 1505 if (!Double.isNaN(value)) { 1506 total = total + value; 1507 } 1508 } 1509 return total; 1510 } 1511 1512 /** 1513 * Calculates the range of values for a dataset where each item is the 1514 * running total of the items for the current series. 1515 * 1516 * @param dataset the dataset (<code>null</code> not permitted). 1517 * 1518 * @return The range. 1519 */ 1520 public static Range findCumulativeRangeBounds(CategoryDataset dataset) { 1521 1522 if (dataset == null) { 1523 throw new IllegalArgumentException("Null 'dataset' argument."); 1524 } 1525 1526 boolean allItemsNull = true; // we'll set this to false if there is at 1527 // least one non-null data item... 1528 double minimum = 0.0; 1529 double maximum = 0.0; 1530 for (int row = 0; row < dataset.getRowCount(); row++) { 1531 double runningTotal = 0.0; 1532 for (int column = 0; column < dataset.getColumnCount() - 1; 1533 column++) { 1534 Number n = dataset.getValue(row, column); 1535 if (n != null) { 1536 allItemsNull = false; 1537 double value = n.doubleValue(); 1538 runningTotal = runningTotal + value; 1539 minimum = Math.min(minimum, runningTotal); 1540 maximum = Math.max(maximum, runningTotal); 1541 } 1542 } 1543 } 1544 if (!allItemsNull) { 1545 return new Range(minimum, maximum); 1546 } 1547 else { 1548 return null; 1549 } 1550 1551 } 1552 1553 }