001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2020, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ---------------------------------------- 028 * DefaultBoxAndWhiskerCategoryDataset.java 029 * ---------------------------------------- 030 * (C) Copyright 2003-2020, by David Browning and Contributors. 031 * 032 * Original Author: David Browning (for Australian Institute of Marine 033 * Science); 034 * Contributor(s): David Gilbert (for Object Refinery Limited); 035 * 036 */ 037 038package org.jfree.data.statistics; 039 040import java.util.List; 041import org.jfree.chart.util.ObjectUtils; 042import org.jfree.chart.util.PublicCloneable; 043 044import org.jfree.data.KeyedObjects2D; 045import org.jfree.data.Range; 046import org.jfree.data.RangeInfo; 047import org.jfree.data.general.AbstractDataset; 048import org.jfree.data.general.DatasetChangeEvent; 049 050/** 051 * A convenience class that provides a default implementation of the 052 * {@link BoxAndWhiskerCategoryDataset} interface. 053 */ 054public class DefaultBoxAndWhiskerCategoryDataset extends AbstractDataset 055 implements BoxAndWhiskerCategoryDataset, RangeInfo, PublicCloneable { 056 057 /** Storage for the data. */ 058 protected KeyedObjects2D data; 059 060 /** The minimum range value. */ 061 private double minimumRangeValue; 062 063 /** The row index for the cell that the minimum range value comes from. */ 064 private int minimumRangeValueRow; 065 066 /** 067 * The column index for the cell that the minimum range value comes from. 068 */ 069 private int minimumRangeValueColumn; 070 071 /** The maximum range value. */ 072 private double maximumRangeValue; 073 074 /** The row index for the cell that the maximum range value comes from. */ 075 private int maximumRangeValueRow; 076 077 /** 078 * The column index for the cell that the maximum range value comes from. 079 */ 080 private int maximumRangeValueColumn; 081 082 /** 083 * Creates a new dataset. 084 */ 085 public DefaultBoxAndWhiskerCategoryDataset() { 086 this.data = new KeyedObjects2D(); 087 this.minimumRangeValue = Double.NaN; 088 this.minimumRangeValueRow = -1; 089 this.minimumRangeValueColumn = -1; 090 this.maximumRangeValue = Double.NaN; 091 this.maximumRangeValueRow = -1; 092 this.maximumRangeValueColumn = -1; 093 } 094 095 /** 096 * Adds a list of values relating to one box-and-whisker entity to the 097 * table. The various median values are calculated. 098 * 099 * @param list a collection of values from which the various medians will 100 * be calculated. 101 * @param rowKey the row key ({@code null} not permitted). 102 * @param columnKey the column key ({@code null} not permitted). 103 * 104 * @see #add(BoxAndWhiskerItem, Comparable, Comparable) 105 */ 106 public void add(List list, Comparable rowKey, Comparable columnKey) { 107 BoxAndWhiskerItem item = BoxAndWhiskerCalculator 108 .calculateBoxAndWhiskerStatistics(list); 109 add(item, rowKey, columnKey); 110 } 111 112 /** 113 * Adds a list of values relating to one Box and Whisker entity to the 114 * table. The various median values are calculated. 115 * 116 * @param item a box and whisker item ({@code null} not permitted). 117 * @param rowKey the row key ({@code null} not permitted). 118 * @param columnKey the column key ({@code null} not permitted). 119 * 120 * @see #add(List, Comparable, Comparable) 121 */ 122 public void add(BoxAndWhiskerItem item, Comparable rowKey, 123 Comparable columnKey) { 124 125 this.data.addObject(item, rowKey, columnKey); 126 127 // update cached min and max values 128 int r = this.data.getRowIndex(rowKey); 129 int c = this.data.getColumnIndex(columnKey); 130 if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn 131 == c) || (this.minimumRangeValueRow == r 132 && this.minimumRangeValueColumn == c)) { 133 updateBounds(); 134 } 135 else { 136 137 double minval = Double.NaN; 138 if (item.getMinOutlier() != null) { 139 minval = item.getMinOutlier().doubleValue(); 140 } 141 double maxval = Double.NaN; 142 if (item.getMaxOutlier() != null) { 143 maxval = item.getMaxOutlier().doubleValue(); 144 } 145 146 if (Double.isNaN(this.maximumRangeValue)) { 147 this.maximumRangeValue = maxval; 148 this.maximumRangeValueRow = r; 149 this.maximumRangeValueColumn = c; 150 } 151 else if (maxval > this.maximumRangeValue) { 152 this.maximumRangeValue = maxval; 153 this.maximumRangeValueRow = r; 154 this.maximumRangeValueColumn = c; 155 } 156 157 if (Double.isNaN(this.minimumRangeValue)) { 158 this.minimumRangeValue = minval; 159 this.minimumRangeValueRow = r; 160 this.minimumRangeValueColumn = c; 161 } 162 else if (minval < this.minimumRangeValue) { 163 this.minimumRangeValue = minval; 164 this.minimumRangeValueRow = r; 165 this.minimumRangeValueColumn = c; 166 } 167 } 168 169 fireDatasetChanged(); 170 171 } 172 173 /** 174 * Removes an item from the dataset and sends a {@link DatasetChangeEvent} 175 * to all registered listeners. 176 * 177 * @param rowKey the row key ({@code null} not permitted). 178 * @param columnKey the column key ({@code null} not permitted). 179 * 180 * @see #add(BoxAndWhiskerItem, Comparable, Comparable) 181 * 182 * @since 1.0.7 183 */ 184 public void remove(Comparable rowKey, Comparable columnKey) { 185 // defer null argument checks 186 int r = getRowIndex(rowKey); 187 int c = getColumnIndex(columnKey); 188 this.data.removeObject(rowKey, columnKey); 189 190 // if this cell held a maximum and/or minimum value, we'll need to 191 // update the cached bounds... 192 if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn 193 == c) || (this.minimumRangeValueRow == r 194 && this.minimumRangeValueColumn == c)) { 195 updateBounds(); 196 } 197 198 fireDatasetChanged(); 199 } 200 201 /** 202 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 203 * to all registered listeners. 204 * 205 * @param rowIndex the row index. 206 * 207 * @see #removeColumn(int) 208 * 209 * @since 1.0.7 210 */ 211 public void removeRow(int rowIndex) { 212 this.data.removeRow(rowIndex); 213 updateBounds(); 214 fireDatasetChanged(); 215 } 216 217 /** 218 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 219 * to all registered listeners. 220 * 221 * @param rowKey the row key. 222 * 223 * @see #removeColumn(Comparable) 224 * 225 * @since 1.0.7 226 */ 227 public void removeRow(Comparable rowKey) { 228 this.data.removeRow(rowKey); 229 updateBounds(); 230 fireDatasetChanged(); 231 } 232 233 /** 234 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 235 * to all registered listeners. 236 * 237 * @param columnIndex the column index. 238 * 239 * @see #removeRow(int) 240 * 241 * @since 1.0.7 242 */ 243 public void removeColumn(int columnIndex) { 244 this.data.removeColumn(columnIndex); 245 updateBounds(); 246 fireDatasetChanged(); 247 } 248 249 /** 250 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 251 * to all registered listeners. 252 * 253 * @param columnKey the column key. 254 * 255 * @see #removeRow(Comparable) 256 * 257 * @since 1.0.7 258 */ 259 public void removeColumn(Comparable columnKey) { 260 this.data.removeColumn(columnKey); 261 updateBounds(); 262 fireDatasetChanged(); 263 } 264 265 /** 266 * Clears all data from the dataset and sends a {@link DatasetChangeEvent} 267 * to all registered listeners. 268 * 269 * @since 1.0.7 270 */ 271 public void clear() { 272 this.data.clear(); 273 updateBounds(); 274 fireDatasetChanged(); 275 } 276 277 /** 278 * Return an item from within the dataset. 279 * 280 * @param row the row index. 281 * @param column the column index. 282 * 283 * @return The item. 284 */ 285 public BoxAndWhiskerItem getItem(int row, int column) { 286 return (BoxAndWhiskerItem) this.data.getObject(row, column); 287 } 288 289 /** 290 * Returns the value for an item. 291 * 292 * @param row the row index. 293 * @param column the column index. 294 * 295 * @return The value. 296 * 297 * @see #getMedianValue(int, int) 298 * @see #getValue(Comparable, Comparable) 299 */ 300 @Override 301 public Number getValue(int row, int column) { 302 return getMedianValue(row, column); 303 } 304 305 /** 306 * Returns the value for an item. 307 * 308 * @param rowKey the row key. 309 * @param columnKey the columnKey. 310 * 311 * @return The value. 312 * 313 * @see #getMedianValue(Comparable, Comparable) 314 * @see #getValue(int, int) 315 */ 316 @Override 317 public Number getValue(Comparable rowKey, Comparable columnKey) { 318 return getMedianValue(rowKey, columnKey); 319 } 320 321 /** 322 * Returns the mean value for an item. 323 * 324 * @param row the row index (zero-based). 325 * @param column the column index (zero-based). 326 * 327 * @return The mean value. 328 * 329 * @see #getItem(int, int) 330 */ 331 @Override 332 public Number getMeanValue(int row, int column) { 333 334 Number result = null; 335 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, 336 column); 337 if (item != null) { 338 result = item.getMean(); 339 } 340 return result; 341 342 } 343 344 /** 345 * Returns the mean value for an item. 346 * 347 * @param rowKey the row key. 348 * @param columnKey the column key. 349 * 350 * @return The mean value. 351 * 352 * @see #getItem(int, int) 353 */ 354 @Override 355 public Number getMeanValue(Comparable rowKey, Comparable columnKey) { 356 Number result = null; 357 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 358 rowKey, columnKey); 359 if (item != null) { 360 result = item.getMean(); 361 } 362 return result; 363 } 364 365 /** 366 * Returns the median value for an item. 367 * 368 * @param row the row index (zero-based). 369 * @param column the column index (zero-based). 370 * 371 * @return The median value. 372 * 373 * @see #getItem(int, int) 374 */ 375 @Override 376 public Number getMedianValue(int row, int column) { 377 Number result = null; 378 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, 379 column); 380 if (item != null) { 381 result = item.getMedian(); 382 } 383 return result; 384 } 385 386 /** 387 * Returns the median value for an item. 388 * 389 * @param rowKey the row key. 390 * @param columnKey the columnKey. 391 * 392 * @return The median value. 393 * 394 * @see #getItem(int, int) 395 */ 396 @Override 397 public Number getMedianValue(Comparable rowKey, Comparable columnKey) { 398 Number result = null; 399 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 400 rowKey, columnKey); 401 if (item != null) { 402 result = item.getMedian(); 403 } 404 return result; 405 } 406 407 /** 408 * Returns the first quartile value. 409 * 410 * @param row the row index (zero-based). 411 * @param column the column index (zero-based). 412 * 413 * @return The first quartile value. 414 * 415 * @see #getItem(int, int) 416 */ 417 @Override 418 public Number getQ1Value(int row, int column) { 419 Number result = null; 420 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 421 row, column); 422 if (item != null) { 423 result = item.getQ1(); 424 } 425 return result; 426 } 427 428 /** 429 * Returns the first quartile value. 430 * 431 * @param rowKey the row key. 432 * @param columnKey the column key. 433 * 434 * @return The first quartile value. 435 * 436 * @see #getItem(int, int) 437 */ 438 @Override 439 public Number getQ1Value(Comparable rowKey, Comparable columnKey) { 440 Number result = null; 441 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 442 rowKey, columnKey); 443 if (item != null) { 444 result = item.getQ1(); 445 } 446 return result; 447 } 448 449 /** 450 * Returns the third quartile value. 451 * 452 * @param row the row index (zero-based). 453 * @param column the column index (zero-based). 454 * 455 * @return The third quartile value. 456 * 457 * @see #getItem(int, int) 458 */ 459 @Override 460 public Number getQ3Value(int row, int column) { 461 Number result = null; 462 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 463 row, column); 464 if (item != null) { 465 result = item.getQ3(); 466 } 467 return result; 468 } 469 470 /** 471 * Returns the third quartile value. 472 * 473 * @param rowKey the row key. 474 * @param columnKey the column key. 475 * 476 * @return The third quartile value. 477 * 478 * @see #getItem(int, int) 479 */ 480 @Override 481 public Number getQ3Value(Comparable rowKey, Comparable columnKey) { 482 Number result = null; 483 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 484 rowKey, columnKey); 485 if (item != null) { 486 result = item.getQ3(); 487 } 488 return result; 489 } 490 491 /** 492 * Returns the column index for a given key. 493 * 494 * @param key the column key ({@code null} not permitted). 495 * 496 * @return The column index. 497 * 498 * @see #getColumnKey(int) 499 */ 500 @Override 501 public int getColumnIndex(Comparable key) { 502 return this.data.getColumnIndex(key); 503 } 504 505 /** 506 * Returns a column key. 507 * 508 * @param column the column index (zero-based). 509 * 510 * @return The column key. 511 * 512 * @see #getColumnIndex(Comparable) 513 */ 514 @Override 515 public Comparable getColumnKey(int column) { 516 return this.data.getColumnKey(column); 517 } 518 519 /** 520 * Returns the column keys. 521 * 522 * @return The keys. 523 * 524 * @see #getRowKeys() 525 */ 526 @Override 527 public List getColumnKeys() { 528 return this.data.getColumnKeys(); 529 } 530 531 /** 532 * Returns the row index for a given key. 533 * 534 * @param key the row key ({@code null} not permitted). 535 * 536 * @return The row index. 537 * 538 * @see #getRowKey(int) 539 */ 540 @Override 541 public int getRowIndex(Comparable key) { 542 // defer null argument check 543 return this.data.getRowIndex(key); 544 } 545 546 /** 547 * Returns a row key. 548 * 549 * @param row the row index (zero-based). 550 * 551 * @return The row key. 552 * 553 * @see #getRowIndex(Comparable) 554 */ 555 @Override 556 public Comparable getRowKey(int row) { 557 return this.data.getRowKey(row); 558 } 559 560 /** 561 * Returns the row keys. 562 * 563 * @return The keys. 564 * 565 * @see #getColumnKeys() 566 */ 567 @Override 568 public List getRowKeys() { 569 return this.data.getRowKeys(); 570 } 571 572 /** 573 * Returns the number of rows in the table. 574 * 575 * @return The row count. 576 * 577 * @see #getColumnCount() 578 */ 579 @Override 580 public int getRowCount() { 581 return this.data.getRowCount(); 582 } 583 584 /** 585 * Returns the number of columns in the table. 586 * 587 * @return The column count. 588 * 589 * @see #getRowCount() 590 */ 591 @Override 592 public int getColumnCount() { 593 return this.data.getColumnCount(); 594 } 595 596 /** 597 * Returns the minimum y-value in the dataset. 598 * 599 * @param includeInterval a flag that determines whether or not the 600 * y-interval is taken into account. 601 * 602 * @return The minimum value. 603 * 604 * @see #getRangeUpperBound(boolean) 605 */ 606 @Override 607 public double getRangeLowerBound(boolean includeInterval) { 608 return this.minimumRangeValue; 609 } 610 611 /** 612 * Returns the maximum y-value in the dataset. 613 * 614 * @param includeInterval a flag that determines whether or not the 615 * y-interval is taken into account. 616 * 617 * @return The maximum value. 618 * 619 * @see #getRangeLowerBound(boolean) 620 */ 621 @Override 622 public double getRangeUpperBound(boolean includeInterval) { 623 return this.maximumRangeValue; 624 } 625 626 /** 627 * Returns the range of the values in this dataset's range. 628 * 629 * @param includeInterval a flag that determines whether or not the 630 * y-interval is taken into account. 631 * 632 * @return The range. 633 */ 634 @Override 635 public Range getRangeBounds(boolean includeInterval) { 636 return new Range(this.minimumRangeValue, this.maximumRangeValue); 637 } 638 639 /** 640 * Returns the minimum regular (non outlier) value for an item. 641 * 642 * @param row the row index (zero-based). 643 * @param column the column index (zero-based). 644 * 645 * @return The minimum regular value. 646 * 647 * @see #getItem(int, int) 648 */ 649 @Override 650 public Number getMinRegularValue(int row, int column) { 651 Number result = null; 652 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 653 row, column); 654 if (item != null) { 655 result = item.getMinRegularValue(); 656 } 657 return result; 658 } 659 660 /** 661 * Returns the minimum regular (non outlier) value for an item. 662 * 663 * @param rowKey the row key. 664 * @param columnKey the column key. 665 * 666 * @return The minimum regular value. 667 * 668 * @see #getItem(int, int) 669 */ 670 @Override 671 public Number getMinRegularValue(Comparable rowKey, Comparable columnKey) { 672 Number result = null; 673 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 674 rowKey, columnKey); 675 if (item != null) { 676 result = item.getMinRegularValue(); 677 } 678 return result; 679 } 680 681 /** 682 * Returns the maximum regular (non outlier) value for an item. 683 * 684 * @param row the row index (zero-based). 685 * @param column the column index (zero-based). 686 * 687 * @return The maximum regular value. 688 * 689 * @see #getItem(int, int) 690 */ 691 @Override 692 public Number getMaxRegularValue(int row, int column) { 693 Number result = null; 694 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 695 row, column); 696 if (item != null) { 697 result = item.getMaxRegularValue(); 698 } 699 return result; 700 } 701 702 /** 703 * Returns the maximum regular (non outlier) value for an item. 704 * 705 * @param rowKey the row key. 706 * @param columnKey the column key. 707 * 708 * @return The maximum regular value. 709 * 710 * @see #getItem(int, int) 711 */ 712 @Override 713 public Number getMaxRegularValue(Comparable rowKey, Comparable columnKey) { 714 Number result = null; 715 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 716 rowKey, columnKey); 717 if (item != null) { 718 result = item.getMaxRegularValue(); 719 } 720 return result; 721 } 722 723 /** 724 * Returns the minimum outlier (non farout) value for an item. 725 * 726 * @param row the row index (zero-based). 727 * @param column the column index (zero-based). 728 * 729 * @return The minimum outlier. 730 * 731 * @see #getItem(int, int) 732 */ 733 @Override 734 public Number getMinOutlier(int row, int column) { 735 Number result = null; 736 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 737 row, column); 738 if (item != null) { 739 result = item.getMinOutlier(); 740 } 741 return result; 742 } 743 744 /** 745 * Returns the minimum outlier (non farout) value for an item. 746 * 747 * @param rowKey the row key. 748 * @param columnKey the column key. 749 * 750 * @return The minimum outlier. 751 * 752 * @see #getItem(int, int) 753 */ 754 @Override 755 public Number getMinOutlier(Comparable rowKey, Comparable columnKey) { 756 Number result = null; 757 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 758 rowKey, columnKey); 759 if (item != null) { 760 result = item.getMinOutlier(); 761 } 762 return result; 763 } 764 765 /** 766 * Returns the maximum outlier (non farout) value for an item. 767 * 768 * @param row the row index (zero-based). 769 * @param column the column index (zero-based). 770 * 771 * @return The maximum outlier. 772 * 773 * @see #getItem(int, int) 774 */ 775 @Override 776 public Number getMaxOutlier(int row, int column) { 777 Number result = null; 778 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 779 row, column); 780 if (item != null) { 781 result = item.getMaxOutlier(); 782 } 783 return result; 784 } 785 786 /** 787 * Returns the maximum outlier (non farout) value for an item. 788 * 789 * @param rowKey the row key. 790 * @param columnKey the column key. 791 * 792 * @return The maximum outlier. 793 * 794 * @see #getItem(int, int) 795 */ 796 @Override 797 public Number getMaxOutlier(Comparable rowKey, Comparable columnKey) { 798 Number result = null; 799 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 800 rowKey, columnKey); 801 if (item != null) { 802 result = item.getMaxOutlier(); 803 } 804 return result; 805 } 806 807 /** 808 * Returns a list of outlier values for an item. 809 * 810 * @param row the row index (zero-based). 811 * @param column the column index (zero-based). 812 * 813 * @return A list of outlier values. 814 * 815 * @see #getItem(int, int) 816 */ 817 @Override 818 public List getOutliers(int row, int column) { 819 List result = null; 820 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 821 row, column); 822 if (item != null) { 823 result = item.getOutliers(); 824 } 825 return result; 826 } 827 828 /** 829 * Returns a list of outlier values for an item. 830 * 831 * @param rowKey the row key. 832 * @param columnKey the column key. 833 * 834 * @return A list of outlier values. 835 * 836 * @see #getItem(int, int) 837 */ 838 @Override 839 public List getOutliers(Comparable rowKey, Comparable columnKey) { 840 List result = null; 841 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 842 rowKey, columnKey); 843 if (item != null) { 844 result = item.getOutliers(); 845 } 846 return result; 847 } 848 849 /** 850 * Resets the cached bounds, by iterating over the entire dataset to find 851 * the current bounds. 852 */ 853 private void updateBounds() { 854 this.minimumRangeValue = Double.NaN; 855 this.minimumRangeValueRow = -1; 856 this.minimumRangeValueColumn = -1; 857 this.maximumRangeValue = Double.NaN; 858 this.maximumRangeValueRow = -1; 859 this.maximumRangeValueColumn = -1; 860 int rowCount = getRowCount(); 861 int columnCount = getColumnCount(); 862 for (int r = 0; r < rowCount; r++) { 863 for (int c = 0; c < columnCount; c++) { 864 BoxAndWhiskerItem item = getItem(r, c); 865 if (item != null) { 866 Number min = item.getMinOutlier(); 867 if (min != null) { 868 double minv = min.doubleValue(); 869 if (!Double.isNaN(minv)) { 870 if (minv < this.minimumRangeValue || Double.isNaN( 871 this.minimumRangeValue)) { 872 this.minimumRangeValue = minv; 873 this.minimumRangeValueRow = r; 874 this.minimumRangeValueColumn = c; 875 } 876 } 877 } 878 Number max = item.getMaxOutlier(); 879 if (max != null) { 880 double maxv = max.doubleValue(); 881 if (!Double.isNaN(maxv)) { 882 if (maxv > this.maximumRangeValue || Double.isNaN( 883 this.maximumRangeValue)) { 884 this.maximumRangeValue = maxv; 885 this.maximumRangeValueRow = r; 886 this.maximumRangeValueColumn = c; 887 } 888 } 889 } 890 } 891 } 892 } 893 } 894 895 /** 896 * Tests this dataset for equality with an arbitrary object. 897 * 898 * @param obj the object to test against ({@code null} permitted). 899 * 900 * @return A boolean. 901 */ 902 @Override 903 public boolean equals(Object obj) { 904 if (obj == this) { 905 return true; 906 } 907 if (obj instanceof DefaultBoxAndWhiskerCategoryDataset) { 908 DefaultBoxAndWhiskerCategoryDataset dataset 909 = (DefaultBoxAndWhiskerCategoryDataset) obj; 910 return ObjectUtils.equal(this.data, dataset.data); 911 } 912 return false; 913 } 914 915 /** 916 * Returns a clone of this dataset. 917 * 918 * @return A clone. 919 * 920 * @throws CloneNotSupportedException if cloning is not possible. 921 */ 922 @Override 923 public Object clone() throws CloneNotSupportedException { 924 DefaultBoxAndWhiskerCategoryDataset clone 925 = (DefaultBoxAndWhiskerCategoryDataset) super.clone(); 926 clone.data = (KeyedObjects2D) this.data.clone(); 927 return clone; 928 } 929 930}