Source for org.jfree.data.category.DefaultIntervalCategoryDataset

   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:  * DefaultIntervalCategoryDataset.java
  29:  * -----------------------------------
  30:  * (C) Copyright 2002-2007, by Jeremy Bowman and Contributors.
  31:  *
  32:  * Original Author:  Jeremy Bowman;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 29-Apr-2002 : Version 1, contributed by Jeremy Bowman (DG);
  38:  * 24-Oct-2002 : Amendments for changes made to the dataset interface (DG);
  39:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  40:  * 08-Mar-2007 : Added equals() and clone() overrides (DG);
  41:  *
  42:  */
  43: 
  44: package org.jfree.data.category;
  45: 
  46: import java.util.ArrayList;
  47: import java.util.Arrays;
  48: import java.util.Collections;
  49: import java.util.List;
  50: import java.util.ResourceBundle;
  51: 
  52: import org.jfree.data.DataUtilities;
  53: import org.jfree.data.UnknownKeyException;
  54: import org.jfree.data.general.AbstractSeriesDataset;
  55: 
  56: /**
  57:  * A convenience class that provides a default implementation of the
  58:  * {@link IntervalCategoryDataset} interface.
  59:  * <p>
  60:  * The standard constructor accepts data in a two dimensional array where the
  61:  * first dimension is the series, and the second dimension is the category.
  62:  */
  63: public class DefaultIntervalCategoryDataset extends AbstractSeriesDataset
  64:                                             implements IntervalCategoryDataset {
  65: 
  66:     /** The series keys. */
  67:     private Comparable[] seriesKeys;
  68: 
  69:     /** The category keys. */
  70:     private Comparable[] categoryKeys;
  71: 
  72:     /** Storage for the start value data. */
  73:     private Number[][] startData;
  74: 
  75:     /** Storage for the end value data. */
  76:     private Number[][] endData;
  77: 
  78:     /**
  79:      * Creates a new dataset.
  80:      *
  81:      * @param starts  the starting values for the intervals.
  82:      * @param ends  the ending values for the intervals.
  83:      */
  84:     public DefaultIntervalCategoryDataset(double[][] starts, double[][] ends) {
  85:         this(DataUtilities.createNumberArray2D(starts),
  86:                 DataUtilities.createNumberArray2D(ends));
  87:     }
  88: 
  89:     /**
  90:      * Constructs a dataset and populates it with data from the array.
  91:      * <p>
  92:      * The arrays are indexed as data[series][category].  Series and category
  93:      * names are automatically generated - you can change them using the
  94:      * {@link #setSeriesKeys(Comparable[])} and 
  95:      * {@link #setCategoryKeys(Comparable[])} methods.
  96:      *
  97:      * @param starts  the start values data.
  98:      * @param ends  the end values data.
  99:      */
 100:     public DefaultIntervalCategoryDataset(Number[][] starts, Number[][] ends) {
 101:         this(null, null, starts, ends);
 102:     }
 103: 
 104:     /**
 105:      * Constructs a DefaultIntervalCategoryDataset, populates it with data
 106:      * from the arrays, and uses the supplied names for the series.
 107:      * <p>
 108:      * Category names are generated automatically ("Category 1", "Category 2",
 109:      * etc).
 110:      *
 111:      * @param seriesNames  the series names.
 112:      * @param starts  the start values data, indexed as data[series][category].
 113:      * @param ends  the end values data, indexed as data[series][category].
 114:      */
 115:     public DefaultIntervalCategoryDataset(String[] seriesNames,
 116:                                           Number[][] starts,
 117:                                           Number[][] ends) {
 118: 
 119:         this(seriesNames, null, starts, ends);
 120: 
 121:     }
 122: 
 123:     /**
 124:      * Constructs a DefaultIntervalCategoryDataset, populates it with data
 125:      * from the arrays, and uses the supplied names for the series and the
 126:      * supplied objects for the categories.
 127:      *
 128:      * @param seriesKeys the series keys.
 129:      * @param categoryKeys  the categories.
 130:      * @param starts  the start values data, indexed as data[series][category].
 131:      * @param ends  the end values data, indexed as data[series][category].
 132:      */
 133:     public DefaultIntervalCategoryDataset(Comparable[] seriesKeys,
 134:                                           Comparable[] categoryKeys,
 135:                                           Number[][] starts,
 136:                                           Number[][] ends) {
 137: 
 138:         this.startData = starts;
 139:         this.endData = ends;
 140: 
 141:         if (starts != null && ends != null) {
 142: 
 143:             String baseName = "org.jfree.data.resources.DataPackageResources";
 144:             ResourceBundle resources = ResourceBundle.getBundle(baseName);
 145: 
 146:             int seriesCount = starts.length;
 147:             if (seriesCount != ends.length) {
 148:                 String errMsg = "DefaultIntervalCategoryDataset: the number "
 149:                     + "of series in the start value dataset does "
 150:                     + "not match the number of series in the end "
 151:                     + "value dataset.";
 152:                 throw new IllegalArgumentException(errMsg);
 153:             }
 154:             if (seriesCount > 0) {
 155: 
 156:                 // set up the series names...
 157:                 if (seriesKeys != null) {
 158: 
 159:                     if (seriesKeys.length != seriesCount) {
 160:                         throw new IllegalArgumentException(
 161:                                 "The number of series keys does not "
 162:                                 + "match the number of series in the data.");
 163:                     }
 164: 
 165:                     this.seriesKeys = seriesKeys;
 166:                 }
 167:                 else {
 168:                     String prefix = resources.getString(
 169:                             "series.default-prefix") + " ";
 170:                     this.seriesKeys = generateKeys(seriesCount, prefix);
 171:                 }
 172: 
 173:                 // set up the category names...
 174:                 int categoryCount = starts[0].length;
 175:                 if (categoryCount != ends[0].length) {
 176:                     String errMsg = "DefaultIntervalCategoryDataset: the "
 177:                                 + "number of categories in the start value "
 178:                                 + "dataset does not match the number of "
 179:                                 + "categories in the end value dataset.";
 180:                     throw new IllegalArgumentException(errMsg);
 181:                 }
 182:                 if (categoryKeys != null) {
 183:                     if (categoryKeys.length != categoryCount) {
 184:                         throw new IllegalArgumentException(
 185:                                 "The number of category keys does not match "
 186:                                 + "the number of categories in the data.");
 187:                     }
 188:                     this.categoryKeys = categoryKeys;
 189:                 }
 190:                 else {
 191:                     String prefix = resources.getString(
 192:                             "categories.default-prefix") + " ";
 193:                     this.categoryKeys = generateKeys(categoryCount, prefix);
 194:                 }
 195: 
 196:             }
 197:             else {
 198:                 this.seriesKeys = null;
 199:                 this.categoryKeys = null;
 200:             }
 201:         }
 202: 
 203:     }
 204: 
 205:     /**
 206:      * Returns the number of series in the dataset (possibly zero).
 207:      *
 208:      * @return The number of series in the dataset.
 209:      * 
 210:      * @see #getRowCount()
 211:      * @see #getCategoryCount()
 212:      */
 213:     public int getSeriesCount() {
 214:         int result = 0;
 215:         if (this.startData != null) {
 216:             result = this.startData.length;
 217:         }
 218:         return result;
 219:     }
 220: 
 221:     /**
 222:      * Returns a series index.
 223:      *
 224:      * @param seriesKey  the series key.
 225:      *
 226:      * @return The series index.
 227:      * 
 228:      * @see #getRowIndex(Comparable)
 229:      * @see #getSeriesKey(int)
 230:      */
 231:     public int getSeriesIndex(Comparable seriesKey) {
 232:         int result = -1;
 233:         for (int i = 0; i < this.seriesKeys.length; i++) {
 234:             if (seriesKey.equals(this.seriesKeys[i])) {
 235:                 result = i;
 236:                 break;
 237:             }
 238:         }
 239:         return result;
 240:     }
 241: 
 242:     /**
 243:      * Returns the name of the specified series.
 244:      *
 245:      * @param series  the index of the required series (zero-based).
 246:      *
 247:      * @return The name of the specified series.
 248:      * 
 249:      * @see #getSeriesIndex(Comparable)
 250:      */
 251:     public Comparable getSeriesKey(int series) {
 252:         if ((series >= getSeriesCount()) || (series < 0)) {
 253:             throw new IllegalArgumentException("No such series : " + series);
 254:         }
 255:         return this.seriesKeys[series];
 256:     }
 257: 
 258:     /**
 259:      * Sets the names of the series in the dataset.
 260:      *
 261:      * @param seriesKeys  the new keys (<code>null</code> not permitted, the 
 262:      *         length of the array must match the number of series in the 
 263:      *         dataset).
 264:      *         
 265:      * @see #setCategoryKeys(Comparable[])
 266:      */
 267:     public void setSeriesKeys(Comparable[] seriesKeys) {
 268:         if (seriesKeys == null) {
 269:             throw new IllegalArgumentException("Null 'seriesKeys' argument.");
 270:         }
 271:         if (seriesKeys.length != getSeriesCount()) {
 272:             throw new IllegalArgumentException(
 273:                     "The number of series keys does not match the data.");
 274:         }
 275:         this.seriesKeys = seriesKeys;
 276:         fireDatasetChanged();
 277:     }
 278: 
 279:     /**
 280:      * Returns the number of categories in the dataset.
 281:      *
 282:      * @return The number of categories in the dataset.
 283:      * 
 284:      * @see #getColumnCount()
 285:      */
 286:     public int getCategoryCount() {
 287:         int result = 0;
 288:         if (this.startData != null) {
 289:             if (getSeriesCount() > 0) {
 290:                 result = this.startData[0].length;
 291:             }
 292:         }
 293:         return result;
 294:     }
 295:     
 296:     /**
 297:      * Returns a list of the categories in the dataset.  This method supports 
 298:      * the {@link CategoryDataset} interface.
 299:      *
 300:      * @return A list of the categories in the dataset.
 301:      * 
 302:      * @see #getRowKeys()
 303:      */
 304:     public List getColumnKeys() {
 305:         // the CategoryDataset interface expects a list of categories, but
 306:         // we've stored them in an array...
 307:         if (this.categoryKeys == null) {
 308:             return new ArrayList();
 309:         }
 310:         else {
 311:             return Collections.unmodifiableList(Arrays.asList(
 312:                     this.categoryKeys));
 313:         }
 314:     }
 315: 
 316:     /**
 317:      * Sets the categories for the dataset.
 318:      *
 319:      * @param categoryKeys  an array of objects representing the categories in 
 320:      *                      the dataset.
 321:      *                      
 322:      * @see #getRowKeys()
 323:      * @see #setSeriesKeys(Comparable[])
 324:      */
 325:     public void setCategoryKeys(Comparable[] categoryKeys) {
 326:         if (categoryKeys == null) {
 327:             throw new IllegalArgumentException("Null 'categoryKeys' argument.");
 328:         }
 329:         if (categoryKeys.length != this.startData[0].length) {
 330:             throw new IllegalArgumentException(
 331:                     "The number of categories does not match the data.");
 332:         }
 333:         for (int i = 0; i < categoryKeys.length; i++) {
 334:             if (categoryKeys[i] == null) {
 335:                 throw new IllegalArgumentException(
 336:                     "DefaultIntervalCategoryDataset.setCategoryKeys(): "
 337:                     + "null category not permitted.");
 338:             }
 339:         }
 340:         this.categoryKeys = categoryKeys;
 341:         fireDatasetChanged();
 342:     }
 343: 
 344:     /**
 345:      * Returns the data value for one category in a series.
 346:      * <P>
 347:      * This method is part of the CategoryDataset interface.  Not particularly
 348:      * meaningful for this class...returns the end value.
 349:      * 
 350:      * @param series    The required series (zero based index).
 351:      * @param category  The required category.
 352:      * 
 353:      * @return The data value for one category in a series (null possible).
 354:      * 
 355:      * @see #getEndValue(Comparable, Comparable)
 356:      */
 357:     public Number getValue(Comparable series, Comparable category) {
 358:         int seriesIndex = getSeriesIndex(series);
 359:         if (seriesIndex < 0) {
 360:             throw new UnknownKeyException("Unknown 'series' key.");
 361:         }
 362:         int itemIndex = getColumnIndex(category);
 363:         if (itemIndex < 0) {
 364:             throw new UnknownKeyException("Unknown 'category' key.");
 365:         }
 366:         return getValue(seriesIndex, itemIndex);
 367:     }
 368: 
 369:     /**
 370:      * Returns the data value for one category in a series.
 371:      * <P>
 372:      * This method is part of the CategoryDataset interface.  Not particularly
 373:      * meaningful for this class...returns the end value.
 374:      *
 375:      * @param series  the required series (zero based index).
 376:      * @param category  the required category.
 377:      *
 378:      * @return The data value for one category in a series (null possible).
 379:      * 
 380:      * @see #getEndValue(int, int)
 381:      */
 382:     public Number getValue(int series, int category) {
 383:         return getEndValue(series, category);
 384:     }
 385: 
 386:     /**
 387:      * Returns the start data value for one category in a series.
 388:      *
 389:      * @param series  the required series.
 390:      * @param category  the required category.
 391:      *
 392:      * @return The start data value for one category in a series 
 393:      *         (possibly <code>null</code>).
 394:      *         
 395:      * @see #getStartValue(int, int)
 396:      */
 397:     public Number getStartValue(Comparable series, Comparable category) {
 398:         int seriesIndex = getSeriesIndex(series);
 399:         if (seriesIndex < 0) {
 400:             throw new UnknownKeyException("Unknown 'series' key.");
 401:         }
 402:         int itemIndex = getColumnIndex(category);
 403:         if (itemIndex < 0) {
 404:             throw new UnknownKeyException("Unknown 'category' key.");
 405:         }
 406:         return getStartValue(seriesIndex, itemIndex);
 407:     }
 408: 
 409:     /**
 410:      * Returns the start data value for one category in a series.
 411:      *
 412:      * @param series  the required series (zero based index).
 413:      * @param category  the required category.
 414:      *
 415:      * @return The start data value for one category in a series 
 416:      *         (possibly <code>null</code>).
 417:      *         
 418:      * @see #getStartValue(Comparable, Comparable)
 419:      */
 420:     public Number getStartValue(int series, int category) {
 421: 
 422:         // check arguments...
 423:         if ((series < 0) || (series >= getSeriesCount())) {
 424:             throw new IllegalArgumentException(
 425:                 "DefaultIntervalCategoryDataset.getValue(): "
 426:                 + "series index out of range.");
 427:         }
 428: 
 429:         if ((category < 0) || (category >= getCategoryCount())) {
 430:             throw new IllegalArgumentException(
 431:                 "DefaultIntervalCategoryDataset.getValue(): "
 432:                 + "category index out of range.");
 433:         }
 434: 
 435:         // fetch the value...
 436:         return this.startData[series][category];
 437: 
 438:     }
 439: 
 440:     /**
 441:      * Returns the end data value for one category in a series.
 442:      *
 443:      * @param series  the required series.
 444:      * @param category  the required category.
 445:      *
 446:      * @return The end data value for one category in a series (null possible).
 447:      * 
 448:      * @see #getEndValue(int, int)
 449:      */
 450:     public Number getEndValue(Comparable series, Comparable category) {
 451:         int seriesIndex = getSeriesIndex(series);
 452:         if (seriesIndex < 0) {
 453:             throw new UnknownKeyException("Unknown 'series' key.");
 454:         }
 455:         int itemIndex = getColumnIndex(category);
 456:         if (itemIndex < 0) {
 457:             throw new UnknownKeyException("Unknown 'category' key.");
 458:         }
 459:         return getEndValue(seriesIndex, itemIndex);
 460:     }
 461: 
 462:     /**
 463:      * Returns the end data value for one category in a series.
 464:      *
 465:      * @param series  the required series (zero based index).
 466:      * @param category  the required category.
 467:      *
 468:      * @return The end data value for one category in a series (null possible).
 469:      * 
 470:      * @see #getEndValue(Comparable, Comparable)
 471:      */
 472:     public Number getEndValue(int series, int category) {
 473:         if ((series < 0) || (series >= getSeriesCount())) {
 474:             throw new IllegalArgumentException(
 475:                 "DefaultIntervalCategoryDataset.getValue(): "
 476:                 + "series index out of range.");
 477:         }
 478: 
 479:         if ((category < 0) || (category >= getCategoryCount())) {
 480:             throw new IllegalArgumentException(
 481:                 "DefaultIntervalCategoryDataset.getValue(): "
 482:                 + "category index out of range.");
 483:         }
 484: 
 485:         return this.endData[series][category];
 486:     }
 487: 
 488:     /**
 489:      * Sets the start data value for one category in a series.
 490:      * 
 491:      * @param series  the series (zero-based index).
 492:      * @param category  the category.
 493:      * 
 494:      * @param value The value.
 495:      * 
 496:      * @see #setEndValue(int, Comparable, Number)
 497:      */
 498:     public void setStartValue(int series, Comparable category, Number value) {
 499: 
 500:         // does the series exist?
 501:         if ((series < 0) || (series > getSeriesCount() - 1)) {
 502:             throw new IllegalArgumentException(
 503:                 "DefaultIntervalCategoryDataset.setValue: "
 504:                 + "series outside valid range.");
 505:         }
 506: 
 507:         // is the category valid?
 508:         int categoryIndex = getCategoryIndex(category);
 509:         if (categoryIndex < 0) {
 510:             throw new IllegalArgumentException(
 511:                 "DefaultIntervalCategoryDataset.setValue: "
 512:                 + "unrecognised category.");
 513:         }
 514: 
 515:         // update the data...
 516:         this.startData[series][categoryIndex] = value;
 517:         fireDatasetChanged();
 518: 
 519:     }
 520: 
 521:     /**
 522:      * Sets the end data value for one category in a series.
 523:      *
 524:      * @param series  the series (zero-based index).
 525:      * @param category  the category.
 526:      *
 527:      * @param value the value.
 528:      * 
 529:      * @see #setStartValue(int, Comparable, Number)
 530:      */
 531:     public void setEndValue(int series, Comparable category, Number value) {
 532: 
 533:         // does the series exist?
 534:         if ((series < 0) || (series > getSeriesCount() - 1)) {
 535:             throw new IllegalArgumentException(
 536:                 "DefaultIntervalCategoryDataset.setValue: "
 537:                 + "series outside valid range.");
 538:         }
 539: 
 540:         // is the category valid?
 541:         int categoryIndex = getCategoryIndex(category);
 542:         if (categoryIndex < 0) {
 543:             throw new IllegalArgumentException(
 544:                 "DefaultIntervalCategoryDataset.setValue: "
 545:                 + "unrecognised category.");
 546:         }
 547: 
 548:         // update the data...
 549:         this.endData[series][categoryIndex] = value;
 550:         fireDatasetChanged();
 551: 
 552:     }
 553: 
 554:     /**
 555:      * Returns the index for the given category.
 556:      *
 557:      * @param category  the category (<code>null</code> not permitted).
 558:      *
 559:      * @return The index.
 560:      * 
 561:      * @see #getColumnIndex(Comparable)
 562:      */
 563:     public int getCategoryIndex(Comparable category) {
 564:         int result = -1;
 565:         for (int i = 0; i < this.categoryKeys.length; i++) {
 566:             if (category.equals(this.categoryKeys[i])) {
 567:                 result = i;
 568:                 break;
 569:             }
 570:         }
 571:         return result;
 572:     }
 573: 
 574:     /**
 575:      * Generates an array of keys, by appending a space plus an integer
 576:      * (starting with 1) to the supplied prefix string.
 577:      *
 578:      * @param count  the number of keys required.
 579:      * @param prefix  the name prefix.
 580:      *
 581:      * @return An array of <i>prefixN</i> with N = { 1 .. count}.
 582:      */
 583:     private Comparable[] generateKeys(int count, String prefix) {
 584:         Comparable[] result = new Comparable[count];
 585:         String name;
 586:         for (int i = 0; i < count; i++) {
 587:             name = prefix + (i + 1);
 588:             result[i] = name;
 589:         }
 590:         return result;
 591:     }
 592: 
 593:     /**
 594:      * Returns a column key.
 595:      *
 596:      * @param column  the column index.
 597:      *
 598:      * @return The column key.
 599:      * 
 600:      * @see #getRowKey(int)
 601:      */
 602:     public Comparable getColumnKey(int column) {
 603:         return this.categoryKeys[column];
 604:     }
 605: 
 606:     /**
 607:      * Returns a column index.
 608:      *
 609:      * @param columnKey  the column key (<code>null</code> not permitted).
 610:      *
 611:      * @return The column index.
 612:      * 
 613:      * @see #getCategoryIndex(Comparable)
 614:      */
 615:     public int getColumnIndex(Comparable columnKey) {
 616:         if (columnKey == null) {
 617:             throw new IllegalArgumentException("Null 'columnKey' argument.");
 618:         }
 619:         return getCategoryIndex(columnKey);
 620:     }
 621: 
 622:     /**
 623:      * Returns a row index.
 624:      *
 625:      * @param rowKey  the row key.
 626:      *
 627:      * @return The row index.
 628:      * 
 629:      * @see #getSeriesIndex(Comparable)
 630:      */
 631:     public int getRowIndex(Comparable rowKey) {
 632:         return getSeriesIndex(rowKey);
 633:     }
 634: 
 635:     /**
 636:      * Returns a list of the series in the dataset.  This method supports the 
 637:      * {@link CategoryDataset} interface.
 638:      *
 639:      * @return A list of the series in the dataset.
 640:      * 
 641:      * @see #getColumnKeys()
 642:      */
 643:     public List getRowKeys() {
 644:         // the CategoryDataset interface expects a list of series, but
 645:         // we've stored them in an array...
 646:         if (this.seriesKeys == null) {
 647:             return new java.util.ArrayList();
 648:         }
 649:         else {
 650:             return Collections.unmodifiableList(Arrays.asList(this.seriesKeys));
 651:         }
 652:     }
 653: 
 654:     /**
 655:      * Returns the name of the specified series.
 656:      *
 657:      * @param row  the index of the required row/series (zero-based).
 658:      *
 659:      * @return The name of the specified series.
 660:      * 
 661:      * @see #getColumnKey(int)
 662:      */
 663:     public Comparable getRowKey(int row) {
 664:         if ((row >= getRowCount()) || (row < 0)) {
 665:             throw new IllegalArgumentException(
 666:                     "The 'row' argument is out of bounds.");
 667:         }
 668:         return this.seriesKeys[row];
 669:     }
 670: 
 671:     /**
 672:      * Returns the number of categories in the dataset.  This method is part of 
 673:      * the {@link CategoryDataset} interface.
 674:      *
 675:      * @return The number of categories in the dataset.
 676:      * 
 677:      * @see #getCategoryCount()
 678:      * @see #getRowCount()
 679:      */
 680:     public int getColumnCount() {
 681:         return this.categoryKeys.length;
 682:     }
 683: 
 684:     /**
 685:      * Returns the number of series in the dataset (possibly zero).
 686:      *
 687:      * @return The number of series in the dataset.
 688:      * 
 689:      * @see #getSeriesCount()
 690:      * @see #getColumnCount()
 691:      */
 692:     public int getRowCount() {
 693:         return this.seriesKeys.length;
 694:     }
 695:     
 696:     /**
 697:      * Tests this dataset for equality with an arbitrary object.
 698:      * 
 699:      * @param obj  the object (<code>null</code> permitted).
 700:      * 
 701:      * @return A boolean.
 702:      */
 703:     public boolean equals(Object obj) {
 704:         if (obj == this) {
 705:             return true;
 706:         }
 707:         if (!(obj instanceof DefaultIntervalCategoryDataset)) {
 708:             return false;
 709:         }
 710:         DefaultIntervalCategoryDataset that 
 711:                 = (DefaultIntervalCategoryDataset) obj;
 712:         if (!Arrays.equals(this.seriesKeys, that.seriesKeys)) {
 713:             return false;
 714:         }
 715:         if (!Arrays.equals(this.categoryKeys, that.categoryKeys)) {
 716:             return false;
 717:         }
 718:         if (!equal(this.startData, that.startData)) {
 719:             return false;
 720:         }
 721:         if (!equal(this.endData, that.endData)) {
 722:             return false;
 723:         }
 724:         // seem to be the same...
 725:         return true;
 726:     }
 727: 
 728:     /**
 729:      * Returns a clone of this dataset.
 730:      * 
 731:      * @return A clone.
 732:      * 
 733:      * @throws CloneNotSupportedException if there is a problem cloning the
 734:      *         dataset.
 735:      */
 736:     public Object clone() throws CloneNotSupportedException {
 737:         DefaultIntervalCategoryDataset clone 
 738:                 = (DefaultIntervalCategoryDataset) super.clone();
 739:         clone.categoryKeys = (Comparable[]) this.categoryKeys.clone();
 740:         clone.seriesKeys = (Comparable[]) this.seriesKeys.clone();
 741:         clone.startData = clone(this.startData);
 742:         clone.endData = clone(this.endData);
 743:         return clone;
 744:     }
 745:     
 746:     /**
 747:      * Tests two double[][] arrays for equality.
 748:      * 
 749:      * @param array1  the first array (<code>null</code> permitted).
 750:      * @param array2  the second arrray (<code>null</code> permitted).
 751:      * 
 752:      * @return A boolean.
 753:      */
 754:     private static boolean equal(Number[][] array1, Number[][] array2) {
 755:         if (array1 == null) {
 756:             return (array2 == null);
 757:         }
 758:         if (array2 == null) {
 759:             return false;
 760:         }
 761:         if (array1.length != array2.length) {
 762:             return false;
 763:         }
 764:         for (int i = 0; i < array1.length; i++) {
 765:             if (!Arrays.equals(array1[i], array2[i])) {
 766:                 return false;
 767:             }
 768:         }
 769:         return true;
 770:     }
 771:     
 772:     /**
 773:      * Clones a two dimensional array of <code>Number</code> objects.
 774:      * 
 775:      * @param array  the array (<code>null</code> not permitted).
 776:      * 
 777:      * @return A clone of the array.
 778:      */
 779:     private static Number[][] clone(Number[][] array) {
 780:         if (array == null) {
 781:             throw new IllegalArgumentException("Null 'array' argument.");
 782:         }
 783:         Number[][] result = new Number[array.length][];
 784:         for (int i = 0; i < array.length; i++) {
 785:             Number[] child = array[i];
 786:             Number[] copychild = new Number[child.length];
 787:             System.arraycopy(child, 0, copychild, 0, child.length);
 788:             result[i] = copychild;
 789:         }
 790:         return result;
 791:     }
 792: 
 793:     /**
 794:      * Returns a list of the series in the dataset.
 795:      *
 796:      * @return A list of the series in the dataset.
 797:      * 
 798:      * @deprecated Use {@link #getRowKeys()} instead.
 799:      */
 800:     public List getSeries() {
 801:         if (this.seriesKeys == null) {
 802:             return new java.util.ArrayList();
 803:         }
 804:         else {
 805:             return Collections.unmodifiableList(Arrays.asList(this.seriesKeys));
 806:         }
 807:     }
 808: 
 809:     /**
 810:      * Returns a list of the categories in the dataset.
 811:      *
 812:      * @return A list of the categories in the dataset.
 813:      * 
 814:      * @deprecated Use {@link #getColumnKeys()} instead.
 815:      */
 816:     public List getCategories() {
 817:         return getColumnKeys();
 818:     }
 819: 
 820:     /**
 821:      * Returns the item count.
 822:      *
 823:      * @return The item count.
 824:      * 
 825:      * @deprecated Use {@link #getCategoryCount()} instead.
 826:      */
 827:     public int getItemCount() {
 828:         return this.categoryKeys.length;
 829:     }
 830: 
 831: }