Source for org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ----------------------------------------
  28:  * DefaultBoxAndWhiskerCategoryDataset.java
  29:  * ----------------------------------------
  30:  * (C) Copyright 2003-2007, by David Browning and Contributors.
  31:  *
  32:  * Original Author:  David Browning (for Australian Institute of Marine 
  33:  *                   Science);
  34:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  35:  *
  36:  * Changes
  37:  * -------
  38:  * 05-Aug-2003 : Version 1, contributed by David Browning (DG);
  39:  * 27-Aug-2003 : Moved from org.jfree.data --> org.jfree.data.statistics (DG);
  40:  * 12-Nov-2003 : Changed 'data' from private to protected and added a new 'add' 
  41:  *               method as proposed by Tim Bardzil.  Also removed old code (DG);
  42:  * 01-Mar-2004 : Added equals() method (DG);
  43:  * 18-Nov-2004 : Updates for changes in RangeInfo interface (DG);
  44:  * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 
  45:  *               release (DG);
  46:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  47:  * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
  48:  * 17-Apr-2007 : Fixed bug 1701822 (DG);
  49:  * 13-Jun-2007 : Fixed error in previous patch (DG);
  50:  * 28-Sep-2007 : Fixed cloning bug (DG);
  51:  * 02-Oct-2007 : Fixed bug in updating cached bounds (DG);
  52:  * 03-Oct-2007 : Fixed another bug in updating cached bounds, added removal
  53:  *               methods (DG);
  54:  *
  55:  */
  56: 
  57: package org.jfree.data.statistics;
  58: 
  59: import java.util.List;
  60: 
  61: import org.jfree.data.KeyedObjects2D;
  62: import org.jfree.data.Range;
  63: import org.jfree.data.RangeInfo;
  64: import org.jfree.data.general.AbstractDataset;
  65: import org.jfree.data.general.DatasetChangeEvent;
  66: import org.jfree.util.ObjectUtilities;
  67: import org.jfree.util.PublicCloneable;
  68: 
  69: /**
  70:  * A convenience class that provides a default implementation of the
  71:  * {@link BoxAndWhiskerCategoryDataset} interface.
  72:  */
  73: public class DefaultBoxAndWhiskerCategoryDataset extends AbstractDataset
  74:         implements BoxAndWhiskerCategoryDataset, RangeInfo, PublicCloneable {
  75: 
  76:     /** Storage for the data. */
  77:     protected KeyedObjects2D data;
  78: 
  79:     /** The minimum range value. */
  80:     private double minimumRangeValue;
  81:     
  82:     /** The row index for the cell that the minimum range value comes from. */
  83:     private int minimumRangeValueRow;
  84:     
  85:     /** 
  86:      * The column index for the cell that the minimum range value comes from. 
  87:      */
  88:     private int minimumRangeValueColumn;
  89: 
  90:     /** The maximum range value. */
  91:     private double maximumRangeValue;
  92: 
  93:     /** The row index for the cell that the maximum range value comes from. */
  94:     private int maximumRangeValueRow;
  95:     
  96:     /** 
  97:      * The column index for the cell that the maximum range value comes from. 
  98:      */
  99:     private int maximumRangeValueColumn;
 100:     
 101:     /**
 102:      * Creates a new dataset.
 103:      */
 104:     public DefaultBoxAndWhiskerCategoryDataset() {
 105:         this.data = new KeyedObjects2D();
 106:         this.minimumRangeValue = Double.NaN;
 107:         this.minimumRangeValueRow = -1;
 108:         this.minimumRangeValueColumn = -1;
 109:         this.maximumRangeValue = Double.NaN;
 110:         this.maximumRangeValueRow = -1;
 111:         this.maximumRangeValueColumn = -1;
 112:     }
 113: 
 114:     /**
 115:      * Adds a list of values relating to one box-and-whisker entity to the 
 116:      * table.  The various median values are calculated.
 117:      *
 118:      * @param list  a collection of values from which the various medians will 
 119:      *              be calculated.
 120:      * @param rowKey  the row key (<code>null</code> not permitted).
 121:      * @param columnKey  the column key (<code>null</code> not permitted).
 122:      * 
 123:      * @see #add(BoxAndWhiskerItem, Comparable, Comparable)
 124:      */
 125:     public void add(List list, Comparable rowKey, Comparable columnKey) {
 126:         BoxAndWhiskerItem item = BoxAndWhiskerCalculator
 127:                 .calculateBoxAndWhiskerStatistics(list);
 128:         add(item, rowKey, columnKey);
 129:     }
 130:     
 131:     /**
 132:      * Adds a list of values relating to one Box and Whisker entity to the 
 133:      * table.  The various median values are calculated.
 134:      *
 135:      * @param item  a box and whisker item (<code>null</code> not permitted).
 136:      * @param rowKey  the row key (<code>null</code> not permitted).
 137:      * @param columnKey  the column key (<code>null</code> not permitted).
 138:      * 
 139:      * @see #add(List, Comparable, Comparable)
 140:      */
 141:     public void add(BoxAndWhiskerItem item, Comparable rowKey, 
 142:             Comparable columnKey) {
 143: 
 144:         this.data.addObject(item, rowKey, columnKey);
 145:         
 146:         // update cached min and max values
 147:         int r = this.data.getRowIndex(rowKey);
 148:         int c = this.data.getColumnIndex(columnKey);
 149:         if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn 
 150:                 == c) || (this.minimumRangeValueRow == r 
 151:                 && this.minimumRangeValueColumn == c))  {
 152:             updateBounds();
 153:         }
 154:         else {
 155:         
 156:             double minval = Double.NaN;
 157:             if (item.getMinOutlier() != null) {
 158:                 minval = item.getMinOutlier().doubleValue();
 159:             }
 160:             double maxval = Double.NaN;
 161:             if (item.getMaxOutlier() != null) {
 162:                 maxval = item.getMaxOutlier().doubleValue();
 163:             }
 164:         
 165:             if (Double.isNaN(this.maximumRangeValue)) {
 166:                 this.maximumRangeValue = maxval;
 167:                 this.maximumRangeValueRow = r;
 168:                 this.maximumRangeValueColumn = c;
 169:             }
 170:             else if (maxval > this.maximumRangeValue) {
 171:                 this.maximumRangeValue = maxval;
 172:                 this.maximumRangeValueRow = r;
 173:                 this.maximumRangeValueColumn = c;
 174:             }
 175:         
 176:             if (Double.isNaN(this.minimumRangeValue)) {
 177:                 this.minimumRangeValue = minval;
 178:                 this.minimumRangeValueRow = r;
 179:                 this.minimumRangeValueColumn = c;
 180:             }
 181:             else if (minval < this.minimumRangeValue) {
 182:                 this.minimumRangeValue = minval;
 183:                 this.minimumRangeValueRow = r;
 184:                 this.minimumRangeValueColumn = c;
 185:             }
 186:         }
 187:         
 188:         fireDatasetChanged();
 189: 
 190:     }
 191:     
 192:     /**
 193:      * Removes an item from the dataset and sends a {@link DatasetChangeEvent}
 194:      * to all registered listeners.
 195:      *
 196:      * @param rowKey  the row key (<code>null</code> not permitted).
 197:      * @param columnKey  the column key (<code>null</code> not permitted).
 198:      * 
 199:      * @see #add(BoxAndWhiskerItem, Comparable, Comparable)
 200:      * 
 201:      * @since 1.0.7
 202:      */
 203:     public void remove(Comparable rowKey, Comparable columnKey) {
 204:         // defer null argument checks
 205:         int r = getRowIndex(rowKey);
 206:         int c = getColumnIndex(columnKey);
 207:         this.data.removeObject(rowKey, columnKey);
 208:         
 209:         // if this cell held a maximum and/or minimum value, we'll need to
 210:         // update the cached bounds...
 211:         if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn 
 212:                 == c) || (this.minimumRangeValueRow == r 
 213:                 && this.minimumRangeValueColumn == c))  {
 214:             updateBounds();
 215:         }
 216:         
 217:         fireDatasetChanged();
 218:     }
 219: 
 220:     /**
 221:      * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
 222:      * to all registered listeners.
 223:      *
 224:      * @param rowIndex  the row index.
 225:      * 
 226:      * @see #removeColumn(int)
 227:      * 
 228:      * @since 1.0.7
 229:      */
 230:     public void removeRow(int rowIndex) {
 231:         this.data.removeRow(rowIndex);
 232:         updateBounds();
 233:         fireDatasetChanged();
 234:     }
 235: 
 236:     /**
 237:      * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
 238:      * to all registered listeners.
 239:      *
 240:      * @param rowKey  the row key.
 241:      * 
 242:      * @see #removeColumn(Comparable)
 243:      * 
 244:      * @since 1.0.7
 245:      */
 246:     public void removeRow(Comparable rowKey) {
 247:         this.data.removeRow(rowKey);
 248:         updateBounds();
 249:         fireDatasetChanged();
 250:     }
 251: 
 252:     /**
 253:      * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
 254:      * to all registered listeners.
 255:      *
 256:      * @param columnIndex  the column index.
 257:      * 
 258:      * @see #removeRow(int)
 259:      * 
 260:      * @since 1.0.7
 261:      */
 262:     public void removeColumn(int columnIndex) {
 263:         this.data.removeColumn(columnIndex);
 264:         updateBounds();
 265:         fireDatasetChanged();
 266:     }
 267: 
 268:     /**
 269:      * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
 270:      * to all registered listeners.
 271:      *
 272:      * @param columnKey  the column key.
 273:      * 
 274:      * @see #removeRow(Comparable)
 275:      * 
 276:      * @since 1.0.7
 277:      */
 278:     public void removeColumn(Comparable columnKey) {
 279:         this.data.removeColumn(columnKey);
 280:         updateBounds();
 281:         fireDatasetChanged();
 282:     }
 283: 
 284:     /**
 285:      * Clears all data from the dataset and sends a {@link DatasetChangeEvent} 
 286:      * to all registered listeners.
 287:      * 
 288:      * @since 1.0.7
 289:      */
 290:     public void clear() {
 291:         this.data.clear();
 292:         updateBounds();
 293:         fireDatasetChanged();
 294:     }
 295: 
 296:     /**
 297:      * Return an item from within the dataset.
 298:      * 
 299:      * @param row  the row index.
 300:      * @param column  the column index.
 301:      * 
 302:      * @return The item.
 303:      */
 304:     public BoxAndWhiskerItem getItem(int row, int column) {
 305:         return (BoxAndWhiskerItem) this.data.getObject(row, column);  
 306:     }
 307: 
 308:     /**
 309:      * Returns the value for an item.
 310:      *
 311:      * @param row  the row index.
 312:      * @param column  the column index.
 313:      *
 314:      * @return The value.
 315:      * 
 316:      * @see #getMedianValue(int, int)
 317:      * @see #getValue(Comparable, Comparable)
 318:      */
 319:     public Number getValue(int row, int column) {
 320:         return getMedianValue(row, column);
 321:     }
 322: 
 323:     /**
 324:      * Returns the value for an item.
 325:      *
 326:      * @param rowKey  the row key.
 327:      * @param columnKey  the columnKey.
 328:      *
 329:      * @return The value.
 330:      * 
 331:      * @see #getMedianValue(Comparable, Comparable)
 332:      * @see #getValue(int, int)
 333:      */
 334:     public Number getValue(Comparable rowKey, Comparable columnKey) {
 335:         return getMedianValue(rowKey, columnKey);
 336:     }
 337: 
 338:     /**
 339:      * Returns the mean value for an item.
 340:      * 
 341:      * @param row  the row index (zero-based).
 342:      * @param column  the column index (zero-based).
 343:      * 
 344:      * @return The mean value.
 345:      * 
 346:      * @see #getItem(int, int)
 347:      */
 348:     public Number getMeanValue(int row, int column) {
 349: 
 350:         Number result = null;
 351:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, 
 352:                 column);
 353:         if (item != null) {
 354:             result = item.getMean();
 355:         }
 356:         return result;
 357: 
 358:     }
 359: 
 360:     /**
 361:      * Returns the mean value for an item.
 362:      * 
 363:      * @param rowKey  the row key.
 364:      * @param columnKey  the column key.
 365:      * 
 366:      * @return The mean value.
 367:      * 
 368:      * @see #getItem(int, int)
 369:      */
 370:     public Number getMeanValue(Comparable rowKey, Comparable columnKey) {
 371:         Number result = null;
 372:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 373:                 rowKey, columnKey);
 374:         if (item != null) {
 375:             result = item.getMean();
 376:         }
 377:         return result;
 378:     }
 379: 
 380:     /**
 381:      * Returns the median value for an item.
 382:      *
 383:      * @param row  the row index (zero-based).
 384:      * @param column  the column index (zero-based).
 385:      *
 386:      * @return The median value.
 387:      * 
 388:      * @see #getItem(int, int)
 389:      */
 390:     public Number getMedianValue(int row, int column) {
 391:         Number result = null;
 392:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, 
 393:                 column);
 394:         if (item != null) {
 395:             result = item.getMedian();
 396:         }
 397:         return result;
 398:     }
 399: 
 400:     /**
 401:      * Returns the median value for an item.
 402:      *
 403:      * @param rowKey  the row key.
 404:      * @param columnKey  the columnKey.
 405:      *
 406:      * @return The median value.
 407:      * 
 408:      * @see #getItem(int, int)
 409:      */
 410:     public Number getMedianValue(Comparable rowKey, Comparable columnKey) {
 411:         Number result = null;
 412:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 413:                 rowKey, columnKey);
 414:         if (item != null) {
 415:             result = item.getMedian();
 416:         }
 417:         return result;
 418:     }
 419: 
 420:     /**
 421:      * Returns the first quartile value.
 422:      * 
 423:      * @param row  the row index (zero-based).
 424:      * @param column  the column index (zero-based).
 425:      * 
 426:      * @return The first quartile value.
 427:      * 
 428:      * @see #getItem(int, int)
 429:      */
 430:     public Number getQ1Value(int row, int column) {
 431:         Number result = null;
 432:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 433:                 row, column);
 434:         if (item != null) {
 435:             result = item.getQ1();
 436:         }
 437:         return result;
 438:     }
 439: 
 440:     /**
 441:      * Returns the first quartile value.
 442:      * 
 443:      * @param rowKey  the row key.
 444:      * @param columnKey  the column key.
 445:      * 
 446:      * @return The first quartile value.
 447:      * 
 448:      * @see #getItem(int, int)
 449:      */
 450:     public Number getQ1Value(Comparable rowKey, Comparable columnKey) {
 451:         Number result = null;
 452:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 453:                 rowKey, columnKey);
 454:         if (item != null) {
 455:             result = item.getQ1();
 456:         }
 457:         return result;
 458:     }
 459: 
 460:     /**
 461:      * Returns the third quartile value.
 462:      * 
 463:      * @param row  the row index (zero-based).
 464:      * @param column  the column index (zero-based).
 465:      * 
 466:      * @return The third quartile value.
 467:      * 
 468:      * @see #getItem(int, int)
 469:      */
 470:     public Number getQ3Value(int row, int column) {
 471:         Number result = null;
 472:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 473:                 row, column);
 474:         if (item != null) {
 475:             result = item.getQ3();
 476:         }
 477:         return result;
 478:     }
 479: 
 480:     /**
 481:      * Returns the third quartile value.
 482:      * 
 483:      * @param rowKey  the row key.
 484:      * @param columnKey  the column key.
 485:      * 
 486:      * @return The third quartile value.
 487:      * 
 488:      * @see #getItem(int, int)
 489:      */
 490:     public Number getQ3Value(Comparable rowKey, Comparable columnKey) {
 491:         Number result = null;
 492:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 493:                 rowKey, columnKey);
 494:         if (item != null) {
 495:             result = item.getQ3();
 496:         }
 497:         return result;
 498:     }
 499: 
 500:     /**
 501:      * Returns the column index for a given key.
 502:      *
 503:      * @param key  the column key (<code>null</code> not permitted).
 504:      *
 505:      * @return The column index.
 506:      * 
 507:      * @see #getColumnKey(int)
 508:      */
 509:     public int getColumnIndex(Comparable key) {
 510:         return this.data.getColumnIndex(key);
 511:     }
 512: 
 513:     /**
 514:      * Returns a column key.
 515:      *
 516:      * @param column  the column index (zero-based).
 517:      *
 518:      * @return The column key.
 519:      * 
 520:      * @see #getColumnIndex(Comparable)
 521:      */
 522:     public Comparable getColumnKey(int column) {
 523:         return this.data.getColumnKey(column);
 524:     }
 525: 
 526:     /**
 527:      * Returns the column keys.
 528:      *
 529:      * @return The keys.
 530:      * 
 531:      * @see #getRowKeys()
 532:      */
 533:     public List getColumnKeys() {
 534:         return this.data.getColumnKeys();
 535:     }
 536: 
 537:     /**
 538:      * Returns the row index for a given key.
 539:      *
 540:      * @param key  the row key (<code>null</code> not permitted).
 541:      *
 542:      * @return The row index.
 543:      * 
 544:      * @see #getRowKey(int)
 545:      */
 546:     public int getRowIndex(Comparable key) {
 547:         // defer null argument check
 548:         return this.data.getRowIndex(key);
 549:     }
 550: 
 551:     /**
 552:      * Returns a row key.
 553:      *
 554:      * @param row  the row index (zero-based).
 555:      *
 556:      * @return The row key.
 557:      * 
 558:      * @see #getRowIndex(Comparable)
 559:      */
 560:     public Comparable getRowKey(int row) {
 561:         return this.data.getRowKey(row);
 562:     }
 563: 
 564:     /**
 565:      * Returns the row keys.
 566:      *
 567:      * @return The keys.
 568:      * 
 569:      * @see #getColumnKeys()
 570:      */
 571:     public List getRowKeys() {
 572:         return this.data.getRowKeys();
 573:     }
 574: 
 575:     /**
 576:      * Returns the number of rows in the table.
 577:      *
 578:      * @return The row count.
 579:      * 
 580:      * @see #getColumnCount()
 581:      */
 582:     public int getRowCount() {
 583:         return this.data.getRowCount();
 584:     }
 585: 
 586:     /**
 587:      * Returns the number of columns in the table.
 588:      *
 589:      * @return The column count.
 590:      * 
 591:      * @see #getRowCount()
 592:      */
 593:     public int getColumnCount() {
 594:         return this.data.getColumnCount();
 595:     }
 596: 
 597:     /**
 598:      * Returns the minimum y-value in the dataset.
 599:      *
 600:      * @param includeInterval  a flag that determines whether or not the
 601:      *                         y-interval is taken into account.
 602:      * 
 603:      * @return The minimum value.
 604:      * 
 605:      * @see #getRangeUpperBound(boolean)
 606:      */
 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:     public double getRangeUpperBound(boolean includeInterval) {
 622:         return this.maximumRangeValue;
 623:     }
 624: 
 625:     /**
 626:      * Returns the range of the values in this dataset's range.
 627:      *
 628:      * @param includeInterval  a flag that determines whether or not the
 629:      *                         y-interval is taken into account.
 630:      * 
 631:      * @return The range.
 632:      */
 633:     public Range getRangeBounds(boolean includeInterval) {
 634:         return new Range(this.minimumRangeValue, this.maximumRangeValue);
 635:     }
 636:     
 637:     /**
 638:      * Returns the minimum regular (non outlier) value for an item.
 639:      * 
 640:      * @param row  the row index (zero-based).
 641:      * @param column  the column index (zero-based).
 642:      * 
 643:      * @return The minimum regular value.
 644:      * 
 645:      * @see #getItem(int, int)
 646:      */
 647:     public Number getMinRegularValue(int row, int column) {
 648:         Number result = null;
 649:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 650:                 row, column);
 651:         if (item != null) {
 652:             result = item.getMinRegularValue();
 653:         }
 654:         return result;
 655:     }
 656: 
 657:     /**
 658:      * Returns the minimum regular (non outlier) value for an item.
 659:      * 
 660:      * @param rowKey  the row key.
 661:      * @param columnKey  the column key.
 662:      * 
 663:      * @return The minimum regular value.
 664:      * 
 665:      * @see #getItem(int, int)
 666:      */
 667:     public Number getMinRegularValue(Comparable rowKey, Comparable columnKey) {
 668:         Number result = null;
 669:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 670:                 rowKey, columnKey);
 671:         if (item != null) {
 672:             result = item.getMinRegularValue();
 673:         }
 674:         return result;
 675:     }
 676: 
 677:     /**
 678:      * Returns the maximum regular (non outlier) value for an item.
 679:      * 
 680:      * @param row  the row index (zero-based).
 681:      * @param column  the column index (zero-based).
 682:      * 
 683:      * @return The maximum regular value.
 684:      * 
 685:      * @see #getItem(int, int)
 686:      */
 687:     public Number getMaxRegularValue(int row, int column) {
 688:         Number result = null;
 689:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 690:                 row, column);
 691:         if (item != null) {
 692:             result = item.getMaxRegularValue();
 693:         }
 694:         return result;
 695:     }
 696: 
 697:     /**
 698:      * Returns the maximum regular (non outlier) value for an item.
 699:      * 
 700:      * @param rowKey  the row key.
 701:      * @param columnKey  the column key.
 702:      * 
 703:      * @return The maximum regular value.
 704:      * 
 705:      * @see #getItem(int, int)
 706:      */
 707:     public Number getMaxRegularValue(Comparable rowKey, Comparable columnKey) {
 708:         Number result = null;
 709:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 710:                 rowKey, columnKey);
 711:         if (item != null) {
 712:             result = item.getMaxRegularValue();
 713:         }
 714:         return result;
 715:     }
 716: 
 717:     /**
 718:      * Returns the minimum outlier (non farout) value for an item.
 719:      * 
 720:      * @param row  the row index (zero-based).
 721:      * @param column  the column index (zero-based).
 722:      * 
 723:      * @return The minimum outlier.
 724:      * 
 725:      * @see #getItem(int, int)
 726:      */
 727:     public Number getMinOutlier(int row, int column) {
 728:         Number result = null;
 729:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 730:                 row, column);
 731:         if (item != null) {
 732:             result = item.getMinOutlier();
 733:         }
 734:         return result;
 735:     }
 736: 
 737:     /**
 738:      * Returns the minimum outlier (non farout) value for an item.
 739:      * 
 740:      * @param rowKey  the row key.
 741:      * @param columnKey  the column key.
 742:      * 
 743:      * @return The minimum outlier.
 744:      * 
 745:      * @see #getItem(int, int)
 746:      */
 747:     public Number getMinOutlier(Comparable rowKey, Comparable columnKey) {
 748:         Number result = null;
 749:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 750:                 rowKey, columnKey);
 751:         if (item != null) {
 752:             result = item.getMinOutlier();
 753:         }
 754:         return result;
 755:     }
 756: 
 757:     /**
 758:      * Returns the maximum outlier (non farout) value for an item.
 759:      * 
 760:      * @param row  the row index (zero-based).
 761:      * @param column  the column index (zero-based).
 762:      * 
 763:      * @return The maximum outlier.
 764:      * 
 765:      * @see #getItem(int, int)
 766:      */
 767:     public Number getMaxOutlier(int row, int column) {
 768:         Number result = null;
 769:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 770:                 row, column);
 771:         if (item != null) {
 772:             result = item.getMaxOutlier();
 773:         }
 774:         return result;
 775:     }
 776: 
 777:     /**
 778:      * Returns the maximum outlier (non farout) value for an item.
 779:      * 
 780:      * @param rowKey  the row key.
 781:      * @param columnKey  the column key.
 782:      * 
 783:      * @return The maximum outlier.
 784:      * 
 785:      * @see #getItem(int, int)
 786:      */
 787:     public Number getMaxOutlier(Comparable rowKey, Comparable columnKey) {
 788:         Number result = null;
 789:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 790:                 rowKey, columnKey);
 791:         if (item != null) {
 792:             result = item.getMaxOutlier();
 793:         }
 794:         return result;
 795:     }
 796: 
 797:     /**
 798:      * Returns a list of outlier values for an item.
 799:      * 
 800:      * @param row  the row index (zero-based).
 801:      * @param column  the column index (zero-based).
 802:      * 
 803:      * @return A list of outlier values.
 804:      * 
 805:      * @see #getItem(int, int)
 806:      */
 807:     public List getOutliers(int row, int column) {
 808:         List result = null;
 809:         BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(
 810:                 row, column);
 811:         if (item != null) {
 812:             result = item.getOutliers();
 813:         }
 814:         return result;
 815:     }
 816: 
 817:     /**
 818:      * Returns a list of outlier values for an item.
 819:      * 
 820:      * @param rowKey  the row key.
 821:      * @param columnKey  the column key.
 822:      * 
 823:      * @return A list of outlier values.
 824:      * 
 825:      * @see #getItem(int, int)
 826:      */
 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</code> permitted).
 887:      * 
 888:      * @return A boolean.
 889:      */
 890:     public boolean equals(Object obj) {
 891:         if (obj == this) {
 892:             return true;   
 893:         }
 894:         if (obj instanceof DefaultBoxAndWhiskerCategoryDataset) {
 895:             DefaultBoxAndWhiskerCategoryDataset dataset 
 896:                     = (DefaultBoxAndWhiskerCategoryDataset) obj;
 897:             return ObjectUtilities.equal(this.data, dataset.data);
 898:         }
 899:         return false;
 900:     }
 901:     
 902:     /**
 903:      * Returns a clone of this dataset.
 904:      * 
 905:      * @return A clone.
 906:      * 
 907:      * @throws CloneNotSupportedException if cloning is not possible.
 908:      */
 909:     public Object clone() throws CloneNotSupportedException {
 910:         DefaultBoxAndWhiskerCategoryDataset clone 
 911:                 = (DefaultBoxAndWhiskerCategoryDataset) super.clone();
 912:         clone.data = (KeyedObjects2D) this.data.clone();
 913:         return clone;
 914:     }
 915: 
 916: }