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