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