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