001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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 * DefaultStatisticalCategoryDataset.java 029 * -------------------------------------- 030 * (C) Copyright 2002-2008, by Pascal Collet and Contributors. 031 * 032 * Original Author: Pascal Collet; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * Changes 036 * ------- 037 * 21-Aug-2002 : Version 1, contributed by Pascal Collet (DG); 038 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 039 * 05-Feb-2003 : Revised implementation to use KeyedObjects2D (DG); 040 * 28-Aug-2003 : Moved from org.jfree.data --> org.jfree.data.statistics (DG); 041 * 06-Oct-2003 : Removed incorrect Javadoc text (DG); 042 * 18-Nov-2004 : Updated for changes in RangeInfo interface (DG); 043 * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 044 * release (DG); 045 * 01-Feb-2005 : Changed minimumRangeValue and maximumRangeValue from Double 046 * to double (DG); 047 * 05-Feb-2005 : Implemented equals() method (DG); 048 * ------------- JFREECHART 1.0.x --------------------------------------------- 049 * 08-Aug-2006 : Reworked implementation of RangeInfo methods (DG); 050 * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG); 051 * 28-Sep-2007 : Fixed cloning bug (DG); 052 * 02-Oct-2007 : Fixed bug updating cached range values (DG); 053 * 054 */ 055 056 package org.jfree.data.statistics; 057 058 import java.util.List; 059 060 import org.jfree.data.KeyedObjects2D; 061 import org.jfree.data.Range; 062 import org.jfree.data.RangeInfo; 063 import org.jfree.data.general.AbstractDataset; 064 import org.jfree.data.general.DatasetChangeEvent; 065 import org.jfree.util.PublicCloneable; 066 067 /** 068 * A convenience class that provides a default implementation of the 069 * {@link StatisticalCategoryDataset} interface. 070 */ 071 public class DefaultStatisticalCategoryDataset extends AbstractDataset 072 implements StatisticalCategoryDataset, RangeInfo, PublicCloneable { 073 074 /** Storage for the data. */ 075 private KeyedObjects2D data; 076 077 /** The minimum range value. */ 078 private double minimumRangeValue; 079 080 /** The row index for the minimum range value. */ 081 private int minimumRangeValueRow; 082 083 /** The column index for the minimum range value. */ 084 private int minimumRangeValueColumn; 085 086 /** The minimum range value including the standard deviation. */ 087 private double minimumRangeValueIncStdDev; 088 089 /** 090 * The row index for the minimum range value (including the standard 091 * deviation). 092 */ 093 private int minimumRangeValueIncStdDevRow; 094 095 /** 096 * The column index for the minimum range value (including the standard 097 * deviation). 098 */ 099 private int minimumRangeValueIncStdDevColumn; 100 101 /** The maximum range value. */ 102 private double maximumRangeValue; 103 104 /** The row index for the maximum range value. */ 105 private int maximumRangeValueRow; 106 107 /** The column index for the maximum range value. */ 108 private int maximumRangeValueColumn; 109 110 /** The maximum range value including the standard deviation. */ 111 private double maximumRangeValueIncStdDev; 112 113 /** 114 * The row index for the maximum range value (including the standard 115 * deviation). 116 */ 117 private int maximumRangeValueIncStdDevRow; 118 119 /** 120 * The column index for the maximum range value (including the standard 121 * deviation). 122 */ 123 private int maximumRangeValueIncStdDevColumn; 124 125 /** 126 * Creates a new dataset. 127 */ 128 public DefaultStatisticalCategoryDataset() { 129 this.data = new KeyedObjects2D(); 130 this.minimumRangeValue = Double.NaN; 131 this.minimumRangeValueRow = -1; 132 this.minimumRangeValueColumn = -1; 133 this.maximumRangeValue = Double.NaN; 134 this.maximumRangeValueRow = -1; 135 this.maximumRangeValueColumn = -1; 136 this.minimumRangeValueIncStdDev = Double.NaN; 137 this.minimumRangeValueIncStdDevRow = -1; 138 this.minimumRangeValueIncStdDevColumn = -1; 139 this.maximumRangeValueIncStdDev = Double.NaN; 140 this.maximumRangeValueIncStdDevRow = -1; 141 this.maximumRangeValueIncStdDevColumn = -1; 142 } 143 144 /** 145 * Returns the mean value for an item. 146 * 147 * @param row the row index (zero-based). 148 * @param column the column index (zero-based). 149 * 150 * @return The mean value (possibly <code>null</code>). 151 */ 152 public Number getMeanValue(int row, int column) { 153 Number result = null; 154 MeanAndStandardDeviation masd = (MeanAndStandardDeviation) 155 this.data.getObject(row, column); 156 if (masd != null) { 157 result = masd.getMean(); 158 } 159 return result; 160 } 161 162 /** 163 * Returns the value for an item (for this dataset, the mean value is 164 * returned). 165 * 166 * @param row the row index. 167 * @param column the column index. 168 * 169 * @return The value (possibly <code>null</code>). 170 */ 171 public Number getValue(int row, int column) { 172 return getMeanValue(row, column); 173 } 174 175 /** 176 * Returns the value for an item (for this dataset, the mean value is 177 * returned). 178 * 179 * @param rowKey the row key. 180 * @param columnKey the columnKey. 181 * 182 * @return The value (possibly <code>null</code>). 183 */ 184 public Number getValue(Comparable rowKey, Comparable columnKey) { 185 return getMeanValue(rowKey, columnKey); 186 } 187 188 /** 189 * Returns the mean value for an item. 190 * 191 * @param rowKey the row key. 192 * @param columnKey the columnKey. 193 * 194 * @return The mean value (possibly <code>null</code>). 195 */ 196 public Number getMeanValue(Comparable rowKey, Comparable columnKey) { 197 Number result = null; 198 MeanAndStandardDeviation masd = (MeanAndStandardDeviation) 199 this.data.getObject(rowKey, columnKey); 200 if (masd != null) { 201 result = masd.getMean(); 202 } 203 return result; 204 } 205 206 /** 207 * Returns the standard deviation value for an item. 208 * 209 * @param row the row index (zero-based). 210 * @param column the column index (zero-based). 211 * 212 * @return The standard deviation (possibly <code>null</code>). 213 */ 214 public Number getStdDevValue(int row, int column) { 215 Number result = null; 216 MeanAndStandardDeviation masd = (MeanAndStandardDeviation) 217 this.data.getObject(row, column); 218 if (masd != null) { 219 result = masd.getStandardDeviation(); 220 } 221 return result; 222 } 223 224 /** 225 * Returns the standard deviation value for an item. 226 * 227 * @param rowKey the row key. 228 * @param columnKey the columnKey. 229 * 230 * @return The standard deviation (possibly <code>null</code>). 231 */ 232 public Number getStdDevValue(Comparable rowKey, Comparable columnKey) { 233 Number result = null; 234 MeanAndStandardDeviation masd = (MeanAndStandardDeviation) 235 this.data.getObject(rowKey, columnKey); 236 if (masd != null) { 237 result = masd.getStandardDeviation(); 238 } 239 return result; 240 } 241 242 /** 243 * Returns the column index for a given key. 244 * 245 * @param key the column key (<code>null</code> not permitted). 246 * 247 * @return The column index. 248 */ 249 public int getColumnIndex(Comparable key) { 250 // defer null argument check 251 return this.data.getColumnIndex(key); 252 } 253 254 /** 255 * Returns a column key. 256 * 257 * @param column the column index (zero-based). 258 * 259 * @return The column key. 260 */ 261 public Comparable getColumnKey(int column) { 262 return this.data.getColumnKey(column); 263 } 264 265 /** 266 * Returns the column keys. 267 * 268 * @return The keys. 269 */ 270 public List getColumnKeys() { 271 return this.data.getColumnKeys(); 272 } 273 274 /** 275 * Returns the row index for a given key. 276 * 277 * @param key the row key (<code>null</code> not permitted). 278 * 279 * @return The row index. 280 */ 281 public int getRowIndex(Comparable key) { 282 // defer null argument check 283 return this.data.getRowIndex(key); 284 } 285 286 /** 287 * Returns a row key. 288 * 289 * @param row the row index (zero-based). 290 * 291 * @return The row key. 292 */ 293 public Comparable getRowKey(int row) { 294 return this.data.getRowKey(row); 295 } 296 297 /** 298 * Returns the row keys. 299 * 300 * @return The keys. 301 */ 302 public List getRowKeys() { 303 return this.data.getRowKeys(); 304 } 305 306 /** 307 * Returns the number of rows in the table. 308 * 309 * @return The row count. 310 * 311 * @see #getColumnCount() 312 */ 313 public int getRowCount() { 314 return this.data.getRowCount(); 315 } 316 317 /** 318 * Returns the number of columns in the table. 319 * 320 * @return The column count. 321 * 322 * @see #getRowCount() 323 */ 324 public int getColumnCount() { 325 return this.data.getColumnCount(); 326 } 327 328 /** 329 * Adds a mean and standard deviation to the table. 330 * 331 * @param mean the mean. 332 * @param standardDeviation the standard deviation. 333 * @param rowKey the row key. 334 * @param columnKey the column key. 335 */ 336 public void add(double mean, double standardDeviation, 337 Comparable rowKey, Comparable columnKey) { 338 add(new Double(mean), new Double(standardDeviation), rowKey, columnKey); 339 } 340 341 /** 342 * Adds a mean and standard deviation to the table. 343 * 344 * @param mean the mean. 345 * @param standardDeviation the standard deviation. 346 * @param rowKey the row key. 347 * @param columnKey the column key. 348 */ 349 public void add(Number mean, Number standardDeviation, 350 Comparable rowKey, Comparable columnKey) { 351 MeanAndStandardDeviation item = new MeanAndStandardDeviation( 352 mean, standardDeviation); 353 this.data.addObject(item, rowKey, columnKey); 354 355 double m = Double.NaN; 356 double sd = Double.NaN; 357 if (mean != null) { 358 m = mean.doubleValue(); 359 } 360 if (standardDeviation != null) { 361 sd = standardDeviation.doubleValue(); 362 } 363 364 // update cached range values 365 int r = this.data.getColumnIndex(columnKey); 366 int c = this.data.getRowIndex(rowKey); 367 if ((r == this.maximumRangeValueRow && c 368 == this.maximumRangeValueColumn) || (r 369 == this.maximumRangeValueIncStdDevRow && c 370 == this.maximumRangeValueIncStdDevColumn) || (r 371 == this.minimumRangeValueRow && c 372 == this.minimumRangeValueColumn) || (r 373 == this.minimumRangeValueIncStdDevRow && c 374 == this.minimumRangeValueIncStdDevColumn)) { 375 376 // iterate over all data items and update mins and maxes 377 updateBounds(); 378 } 379 else { 380 if (!Double.isNaN(m)) { 381 if (Double.isNaN(this.maximumRangeValue) 382 || m > this.maximumRangeValue) { 383 this.maximumRangeValue = m; 384 this.maximumRangeValueRow = r; 385 this.maximumRangeValueColumn = c; 386 } 387 } 388 389 if (!Double.isNaN(m + sd)) { 390 if (Double.isNaN(this.maximumRangeValueIncStdDev) 391 || (m + sd) > this.maximumRangeValueIncStdDev) { 392 this.maximumRangeValueIncStdDev = m + sd; 393 this.maximumRangeValueIncStdDevRow = r; 394 this.maximumRangeValueIncStdDevColumn = c; 395 } 396 } 397 398 if (!Double.isNaN(m)) { 399 if (Double.isNaN(this.minimumRangeValue) 400 || m < this.minimumRangeValue) { 401 this.minimumRangeValue = m; 402 this.minimumRangeValueRow = r; 403 this.minimumRangeValueColumn = c; 404 } 405 } 406 407 if (!Double.isNaN(m - sd)) { 408 if (Double.isNaN(this.minimumRangeValueIncStdDev) 409 || (m - sd) < this.minimumRangeValueIncStdDev) { 410 this.minimumRangeValueIncStdDev = m - sd; 411 this.minimumRangeValueIncStdDevRow = r; 412 this.minimumRangeValueIncStdDevColumn = c; 413 } 414 } 415 } 416 fireDatasetChanged(); 417 } 418 419 /** 420 * Removes an item from the dataset and sends a {@link DatasetChangeEvent} 421 * to all registered listeners. 422 * 423 * @param rowKey the row key (<code>null</code> not permitted). 424 * @param columnKey the column key (<code>null</code> not permitted). 425 * 426 * @see #add(double, double, Comparable, Comparable) 427 * 428 * @since 1.0.7 429 */ 430 public void remove(Comparable rowKey, Comparable columnKey) { 431 // defer null argument checks 432 int r = getRowIndex(rowKey); 433 int c = getColumnIndex(columnKey); 434 this.data.removeObject(rowKey, columnKey); 435 436 // if this cell held a maximum and/or minimum value, we'll need to 437 // update the cached bounds... 438 if ((r == this.maximumRangeValueRow && c 439 == this.maximumRangeValueColumn) || (r 440 == this.maximumRangeValueIncStdDevRow && c 441 == this.maximumRangeValueIncStdDevColumn) || (r 442 == this.minimumRangeValueRow && c 443 == this.minimumRangeValueColumn) || (r 444 == this.minimumRangeValueIncStdDevRow && c 445 == this.minimumRangeValueIncStdDevColumn)) { 446 447 // iterate over all data items and update mins and maxes 448 updateBounds(); 449 } 450 451 fireDatasetChanged(); 452 } 453 454 455 /** 456 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 457 * to all registered listeners. 458 * 459 * @param rowIndex the row index. 460 * 461 * @see #removeColumn(int) 462 * 463 * @since 1.0.7 464 */ 465 public void removeRow(int rowIndex) { 466 this.data.removeRow(rowIndex); 467 updateBounds(); 468 fireDatasetChanged(); 469 } 470 471 /** 472 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 473 * to all registered listeners. 474 * 475 * @param rowKey the row key (<code>null</code> not permitted). 476 * 477 * @see #removeColumn(Comparable) 478 * 479 * @since 1.0.7 480 */ 481 public void removeRow(Comparable rowKey) { 482 this.data.removeRow(rowKey); 483 updateBounds(); 484 fireDatasetChanged(); 485 } 486 487 /** 488 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 489 * to all registered listeners. 490 * 491 * @param columnIndex the column index. 492 * 493 * @see #removeRow(int) 494 * 495 * @since 1.0.7 496 */ 497 public void removeColumn(int columnIndex) { 498 this.data.removeColumn(columnIndex); 499 updateBounds(); 500 fireDatasetChanged(); 501 } 502 503 /** 504 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 505 * to all registered listeners. 506 * 507 * @param columnKey the column key (<code>null</code> not permitted). 508 * 509 * @see #removeRow(Comparable) 510 * 511 * @since 1.0.7 512 */ 513 public void removeColumn(Comparable columnKey) { 514 this.data.removeColumn(columnKey); 515 updateBounds(); 516 fireDatasetChanged(); 517 } 518 519 /** 520 * Clears all data from the dataset and sends a {@link DatasetChangeEvent} 521 * to all registered listeners. 522 * 523 * @since 1.0.7 524 */ 525 public void clear() { 526 this.data.clear(); 527 updateBounds(); 528 fireDatasetChanged(); 529 } 530 531 /** 532 * Iterate over all the data items and update the cached bound values. 533 */ 534 private void updateBounds() { 535 this.maximumRangeValue = Double.NaN; 536 this.maximumRangeValueRow = -1; 537 this.maximumRangeValueColumn = -1; 538 this.minimumRangeValue = Double.NaN; 539 this.minimumRangeValueRow = -1; 540 this.minimumRangeValueColumn = -1; 541 this.maximumRangeValueIncStdDev = Double.NaN; 542 this.maximumRangeValueIncStdDevRow = -1; 543 this.maximumRangeValueIncStdDevColumn = -1; 544 this.minimumRangeValueIncStdDev = Double.NaN; 545 this.minimumRangeValueIncStdDevRow = -1; 546 this.minimumRangeValueIncStdDevColumn = -1; 547 548 int rowCount = this.data.getRowCount(); 549 int columnCount = this.data.getColumnCount(); 550 for (int r = 0; r < rowCount; r++) { 551 for (int c = 0; c < columnCount; c++) { 552 double m = Double.NaN; 553 double sd = Double.NaN; 554 MeanAndStandardDeviation masd = (MeanAndStandardDeviation) 555 this.data.getObject(r, c); 556 if (masd == null) { 557 continue; 558 } 559 m = masd.getMeanValue(); 560 sd = masd.getStandardDeviationValue(); 561 562 if (!Double.isNaN(m)) { 563 564 // update the max value 565 if (Double.isNaN(this.maximumRangeValue)) { 566 this.maximumRangeValue = m; 567 this.maximumRangeValueRow = r; 568 this.maximumRangeValueColumn = c; 569 } 570 else { 571 if (m > this.maximumRangeValue) { 572 this.maximumRangeValue = m; 573 this.maximumRangeValueRow = r; 574 this.maximumRangeValueColumn = c; 575 } 576 } 577 578 // update the min value 579 if (Double.isNaN(this.minimumRangeValue)) { 580 this.minimumRangeValue = m; 581 this.minimumRangeValueRow = r; 582 this.minimumRangeValueColumn = c; 583 } 584 else { 585 if (m < this.minimumRangeValue) { 586 this.minimumRangeValue = m; 587 this.minimumRangeValueRow = r; 588 this.minimumRangeValueColumn = c; 589 } 590 } 591 592 if (!Double.isNaN(sd)) { 593 // update the max value 594 if (Double.isNaN(this.maximumRangeValueIncStdDev)) { 595 this.maximumRangeValueIncStdDev = m + sd; 596 this.maximumRangeValueIncStdDevRow = r; 597 this.maximumRangeValueIncStdDevColumn = c; 598 } 599 else { 600 if (m + sd > this.maximumRangeValueIncStdDev) { 601 this.maximumRangeValueIncStdDev = m + sd; 602 this.maximumRangeValueIncStdDevRow = r; 603 this.maximumRangeValueIncStdDevColumn = c; 604 } 605 } 606 607 // update the min value 608 if (Double.isNaN(this.minimumRangeValueIncStdDev)) { 609 this.minimumRangeValueIncStdDev = m - sd; 610 this.minimumRangeValueIncStdDevRow = r; 611 this.minimumRangeValueIncStdDevColumn = c; 612 } 613 else { 614 if (m - sd < this.minimumRangeValueIncStdDev) { 615 this.minimumRangeValueIncStdDev = m - sd; 616 this.minimumRangeValueIncStdDevRow = r; 617 this.minimumRangeValueIncStdDevColumn = c; 618 } 619 } 620 } 621 } 622 } 623 } 624 } 625 626 /** 627 * Returns the minimum y-value in the dataset. 628 * 629 * @param includeInterval a flag that determines whether or not the 630 * y-interval is taken into account. 631 * 632 * @return The minimum value. 633 * 634 * @see #getRangeUpperBound(boolean) 635 */ 636 public double getRangeLowerBound(boolean includeInterval) { 637 if (includeInterval) { 638 return this.minimumRangeValueIncStdDev; 639 } 640 else { 641 return this.minimumRangeValue; 642 } 643 } 644 645 /** 646 * Returns the maximum y-value in the dataset. 647 * 648 * @param includeInterval a flag that determines whether or not the 649 * y-interval is taken into account. 650 * 651 * @return The maximum value. 652 * 653 * @see #getRangeLowerBound(boolean) 654 */ 655 public double getRangeUpperBound(boolean includeInterval) { 656 if (includeInterval) { 657 return this.maximumRangeValueIncStdDev; 658 } 659 else { 660 return this.maximumRangeValue; 661 } 662 } 663 664 /** 665 * Returns the range of the values in this dataset's range. 666 * 667 * @param includeInterval a flag that determines whether or not the 668 * y-interval is taken into account. 669 * 670 * @return The range. 671 */ 672 public Range getRangeBounds(boolean includeInterval) { 673 Range result = null; 674 if (includeInterval) { 675 if (!Double.isNaN(this.minimumRangeValueIncStdDev) 676 && !Double.isNaN(this.maximumRangeValueIncStdDev)) { 677 result = new Range(this.minimumRangeValueIncStdDev, 678 this.maximumRangeValueIncStdDev); 679 } 680 } 681 else { 682 if (!Double.isNaN(this.minimumRangeValue) 683 && !Double.isNaN(this.maximumRangeValue)) { 684 result = new Range(this.minimumRangeValue, 685 this.maximumRangeValue); 686 } 687 } 688 return result; 689 } 690 691 /** 692 * Tests this instance for equality with an arbitrary object. 693 * 694 * @param obj the object (<code>null</code> permitted). 695 * 696 * @return A boolean. 697 */ 698 public boolean equals(Object obj) { 699 if (obj == this) { 700 return true; 701 } 702 if (!(obj instanceof DefaultStatisticalCategoryDataset)) { 703 return false; 704 } 705 DefaultStatisticalCategoryDataset that 706 = (DefaultStatisticalCategoryDataset) obj; 707 if (!this.data.equals(that.data)) { 708 return false; 709 } 710 return true; 711 } 712 713 /** 714 * Returns a clone of this dataset. 715 * 716 * @return A clone of this dataset. 717 * 718 * @throws CloneNotSupportedException if cloning cannot be completed. 719 */ 720 public Object clone() throws CloneNotSupportedException { 721 DefaultStatisticalCategoryDataset clone 722 = (DefaultStatisticalCategoryDataset) super.clone(); 723 clone.data = (KeyedObjects2D) this.data.clone(); 724 return clone; 725 } 726 }