Source for org.jfree.data.xy.DefaultIntervalXYDataset

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2006, 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:  * DefaultIntervalXYDataset.java
  29:  * -----------------------------
  30:  * (C) Copyright 2006, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: DefaultIntervalXYDataset.java,v 1.1.2.3 2006/11/28 14:06:14 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 23-Oct-2006 : Version 1 (DG);
  40:  * 02-Nov-2006 : Fixed a problem with adding a new series with the same key
  41:  *               as an existing series (see bug 1589392) (DG);
  42:  * 28-Nov-2006 : New override for clone() (DG);
  43:  *
  44:  */
  45: 
  46: package org.jfree.data.xy;
  47: 
  48: import java.util.ArrayList;
  49: import java.util.Arrays;
  50: import java.util.List;
  51: 
  52: import org.jfree.data.general.DatasetChangeEvent;
  53: 
  54: /**
  55:  * A dataset that defines a range (interval) for both the x-values and the
  56:  * y-values.  This implementation uses six arrays to store the x, start-x,
  57:  * end-x, y, start-y and end-y values.
  58:  * <br><br>
  59:  * An alternative implementation of the {@link IntervalXYDataset} interface
  60:  * is provided by the {@link XYIntervalSeriesCollection} class.
  61:  * 
  62:  * @since 1.0.3
  63:  */
  64: public class DefaultIntervalXYDataset extends AbstractIntervalXYDataset {
  65: 
  66:     /**
  67:      * Storage for the series keys.  This list must be kept in sync with the
  68:      * seriesList.
  69:      */
  70:     private List seriesKeys;
  71:     
  72:     /** 
  73:      * Storage for the series in the dataset.  We use a list because the
  74:      * order of the series is significant.  This list must be kept in sync 
  75:      * with the seriesKeys list.
  76:      */ 
  77:     private List seriesList;
  78:     
  79:     /**
  80:      * Creates a new <code>DefaultIntervalXYDataset</code> instance, initially 
  81:      * containing no data.
  82:      */
  83:     public DefaultIntervalXYDataset() {
  84:         this.seriesKeys = new java.util.ArrayList();
  85:         this.seriesList = new java.util.ArrayList();    
  86:     }
  87: 
  88:     /**
  89:      * Returns the number of series in the dataset.
  90:      *
  91:      * @return The series count.
  92:      */
  93:     public int getSeriesCount() {
  94:         return this.seriesList.size();
  95:     }
  96: 
  97:     /**
  98:      * Returns the key for a series.  
  99:      *
 100:      * @param series  the series index (in the range <code>0</code> to 
 101:      *     <code>getSeriesCount() - 1</code>).
 102:      *
 103:      * @return The key for the series.
 104:      * 
 105:      * @throws IllegalArgumentException if <code>series</code> is not in the 
 106:      *     specified range.
 107:      */
 108:     public Comparable getSeriesKey(int series) {
 109:         if ((series < 0) || (series >= getSeriesCount())) {
 110:             throw new IllegalArgumentException("Series index out of bounds");
 111:         }
 112:         return (Comparable) this.seriesKeys.get(series);
 113:     }
 114: 
 115:     /**
 116:      * Returns the number of items in the specified series.
 117:      * 
 118:      * @param series  the series index (in the range <code>0</code> to 
 119:      *     <code>getSeriesCount() - 1</code>).
 120:      * 
 121:      * @return The item count.
 122:      * 
 123:      * @throws IllegalArgumentException if <code>series</code> is not in the 
 124:      *     specified range.
 125:      */
 126:     public int getItemCount(int series) {
 127:         if ((series < 0) || (series >= getSeriesCount())) {
 128:             throw new IllegalArgumentException("Series index out of bounds");
 129:         }
 130:         double[][] seriesArray = (double[][]) this.seriesList.get(series);
 131:         return seriesArray[0].length;
 132:     }
 133: 
 134:     /**
 135:      * Returns the x-value for an item within a series.
 136:      * 
 137:      * @param series  the series index (in the range <code>0</code> to 
 138:      *     <code>getSeriesCount() - 1</code>).
 139:      * @param item  the item index (in the range <code>0</code> to 
 140:      *     <code>getItemCount(series)</code>).
 141:      *     
 142:      * @return The x-value.
 143:      * 
 144:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 145:      *     within the specified range.
 146:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 147:      *     within the specified range.
 148:      * 
 149:      * @see #getX(int, int)
 150:      */
 151:     public double getXValue(int series, int item) {
 152:         double[][] seriesData = (double[][]) this.seriesList.get(series);
 153:         return seriesData[0][item];
 154:     }
 155: 
 156:     /**
 157:      * Returns the y-value for an item within a series.
 158:      * 
 159:      * @param series  the series index (in the range <code>0</code> to 
 160:      *     <code>getSeriesCount() - 1</code>).
 161:      * @param item  the item index (in the range <code>0</code> to 
 162:      *     <code>getItemCount(series)</code>).
 163:      *     
 164:      * @return The y-value.
 165:      * 
 166:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 167:      *     within the specified range.
 168:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 169:      *     within the specified range.
 170:      * 
 171:      * @see #getY(int, int)
 172:      */
 173:     public double getYValue(int series, int item) {
 174:         double[][] seriesData = (double[][]) this.seriesList.get(series);
 175:         return seriesData[3][item];
 176:     }
 177: 
 178:     /**
 179:      * Returns the starting x-value for an item within a series.
 180:      * 
 181:      * @param series  the series index (in the range <code>0</code> to 
 182:      *     <code>getSeriesCount() - 1</code>).
 183:      * @param item  the item index (in the range <code>0</code> to 
 184:      *     <code>getItemCount(series)</code>).
 185:      *     
 186:      * @return The starting x-value.
 187:      * 
 188:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 189:      *     within the specified range.
 190:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 191:      *     within the specified range.
 192:      * 
 193:      * @see #getStartX(int, int)
 194:      */
 195:     public double getStartXValue(int series, int item) {
 196:         double[][] seriesData = (double[][]) this.seriesList.get(series);
 197:         return seriesData[1][item];
 198:     }
 199: 
 200:     /**
 201:      * Returns the ending x-value for an item within a series.
 202:      * 
 203:      * @param series  the series index (in the range <code>0</code> to 
 204:      *     <code>getSeriesCount() - 1</code>).
 205:      * @param item  the item index (in the range <code>0</code> to 
 206:      *     <code>getItemCount(series)</code>).
 207:      *     
 208:      * @return The ending x-value.
 209:      * 
 210:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 211:      *     within the specified range.
 212:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 213:      *     within the specified range.
 214:      * 
 215:      * @see #getEndX(int, int)
 216:      */
 217:     public double getEndXValue(int series, int item) {
 218:         double[][] seriesData = (double[][]) this.seriesList.get(series);
 219:         return seriesData[2][item];
 220:     }
 221: 
 222:     /**
 223:      * Returns the starting y-value for an item within a series.
 224:      * 
 225:      * @param series  the series index (in the range <code>0</code> to 
 226:      *     <code>getSeriesCount() - 1</code>).
 227:      * @param item  the item index (in the range <code>0</code> to 
 228:      *     <code>getItemCount(series)</code>).
 229:      *     
 230:      * @return The starting y-value.
 231:      * 
 232:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 233:      *     within the specified range.
 234:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 235:      *     within the specified range.
 236:      * 
 237:      * @see #getStartY(int, int)
 238:      */
 239:     public double getStartYValue(int series, int item) {
 240:         double[][] seriesData = (double[][]) this.seriesList.get(series);
 241:         return seriesData[4][item];
 242:     }
 243: 
 244:     /**
 245:      * Returns the ending y-value for an item within a series.
 246:      * 
 247:      * @param series  the series index (in the range <code>0</code> to 
 248:      *     <code>getSeriesCount() - 1</code>).
 249:      * @param item  the item index (in the range <code>0</code> to 
 250:      *     <code>getItemCount(series)</code>).
 251:      *     
 252:      * @return The ending y-value.
 253:      * 
 254:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 255:      *     within the specified range.
 256:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 257:      *     within the specified range.
 258:      * 
 259:      * @see #getEndY(int, int)
 260:      */
 261:     public double getEndYValue(int series, int item) {
 262:         double[][] seriesData = (double[][]) this.seriesList.get(series);
 263:         return seriesData[5][item];
 264:     }
 265: 
 266:     /**
 267:      * Returns the ending x-value for an item within a series.
 268:      * 
 269:      * @param series  the series index (in the range <code>0</code> to 
 270:      *     <code>getSeriesCount() - 1</code>).
 271:      * @param item  the item index (in the range <code>0</code> to 
 272:      *     <code>getItemCount(series)</code>).
 273:      *     
 274:      * @return The ending x-value.
 275:      * 
 276:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 277:      *     within the specified range.
 278:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 279:      *     within the specified range.
 280:      * 
 281:      * @see #getEndXValue(int, int)
 282:      */
 283:     public Number getEndX(int series, int item) {
 284:         return new Double(getEndXValue(series, item));
 285:     }
 286: 
 287:     /**
 288:      * Returns the ending y-value for an item within a series.
 289:      * 
 290:      * @param series  the series index (in the range <code>0</code> to 
 291:      *     <code>getSeriesCount() - 1</code>).
 292:      * @param item  the item index (in the range <code>0</code> to 
 293:      *     <code>getItemCount(series)</code>).
 294:      *     
 295:      * @return The ending y-value.
 296:      * 
 297:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 298:      *     within the specified range.
 299:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 300:      *     within the specified range.
 301:      * 
 302:      * @see #getEndYValue(int, int)
 303:      */
 304:     public Number getEndY(int series, int item) {
 305:         return new Double(getEndYValue(series, item));
 306:     }
 307: 
 308:     /**
 309:      * Returns the starting x-value for an item within a series.
 310:      * 
 311:      * @param series  the series index (in the range <code>0</code> to 
 312:      *     <code>getSeriesCount() - 1</code>).
 313:      * @param item  the item index (in the range <code>0</code> to 
 314:      *     <code>getItemCount(series)</code>).
 315:      *     
 316:      * @return The starting x-value.
 317:      * 
 318:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 319:      *     within the specified range.
 320:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 321:      *     within the specified range.
 322:      * 
 323:      * @see #getStartXValue(int, int)
 324:      */
 325:     public Number getStartX(int series, int item) {
 326:         return new Double(getStartXValue(series, item));
 327:     }
 328: 
 329:     /**
 330:      * Returns the starting y-value for an item within a series.
 331:      * 
 332:      * @param series  the series index (in the range <code>0</code> to 
 333:      *     <code>getSeriesCount() - 1</code>).
 334:      * @param item  the item index (in the range <code>0</code> to 
 335:      *     <code>getItemCount(series)</code>).
 336:      *     
 337:      * @return The starting y-value.
 338:      * 
 339:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 340:      *     within the specified range.
 341:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 342:      *     within the specified range.
 343:      * 
 344:      * @see #getStartYValue(int, int)
 345:      */
 346:     public Number getStartY(int series, int item) {
 347:         return new Double(getStartYValue(series, item));
 348:     }
 349: 
 350:     /**
 351:      * Returns the x-value for an item within a series.
 352:      * 
 353:      * @param series  the series index (in the range <code>0</code> to 
 354:      *     <code>getSeriesCount() - 1</code>).
 355:      * @param item  the item index (in the range <code>0</code> to 
 356:      *     <code>getItemCount(series)</code>).
 357:      *     
 358:      * @return The x-value.
 359:      * 
 360:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 361:      *     within the specified range.
 362:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 363:      *     within the specified range.
 364:      * 
 365:      * @see #getXValue(int, int)
 366:      */
 367:     public Number getX(int series, int item) {
 368:         return new Double(getXValue(series, item));
 369:     }
 370: 
 371:     /**
 372:      * Returns the y-value for an item within a series.
 373:      * 
 374:      * @param series  the series index (in the range <code>0</code> to 
 375:      *     <code>getSeriesCount() - 1</code>).
 376:      * @param item  the item index (in the range <code>0</code> to 
 377:      *     <code>getItemCount(series)</code>).
 378:      *     
 379:      * @return The y-value.
 380:      * 
 381:      * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 
 382:      *     within the specified range.
 383:      * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 
 384:      *     within the specified range.
 385:      * 
 386:      * @see #getYValue(int, int)
 387:      */
 388:     public Number getY(int series, int item) {
 389:         return new Double(getYValue(series, item));
 390:     }
 391:     
 392:     /**
 393:      * Adds a series or if a series with the same key already exists replaces
 394:      * the data for that series, then sends a {@link DatasetChangeEvent} to 
 395:      * all registered listeners.
 396:      * 
 397:      * @param seriesKey  the series key (<code>null</code> not permitted).
 398:      * @param data  the data (must be an array with length 6, containing six 
 399:      *     arrays of equal length, the first containing the x-values and the
 400:      *     second containing the y-values). 
 401:      */
 402:     public void addSeries(Comparable seriesKey, double[][] data) {
 403:         if (seriesKey == null) {
 404:             throw new IllegalArgumentException(
 405:                     "The 'seriesKey' cannot be null.");
 406:         }
 407:         if (data == null) {
 408:             throw new IllegalArgumentException("The 'data' is null.");
 409:         }
 410:         if (data.length != 6) {
 411:             throw new IllegalArgumentException(
 412:                     "The 'data' array must have length == 6.");
 413:         }
 414:         int length = data[0].length;
 415:         if (length != data[1].length || length != data[2].length 
 416:                 || length != data[3].length || length != data[4].length 
 417:                 || length != data[5].length) {
 418:             throw new IllegalArgumentException(
 419:                 "The 'data' array must contain two arrays with equal length.");
 420:         }
 421:         int seriesIndex = indexOf(seriesKey);
 422:         if (seriesIndex == -1) {  // add a new series
 423:             this.seriesKeys.add(seriesKey);
 424:             this.seriesList.add(data);
 425:         }
 426:         else {  // replace an existing series
 427:             this.seriesList.remove(seriesIndex);
 428:             this.seriesList.add(seriesIndex, data);
 429:         }
 430:         notifyListeners(new DatasetChangeEvent(this, this));
 431:     }
 432:     
 433:     /**
 434:      * Tests this <code>DefaultIntervalXYDataset</code> instance for equality 
 435:      * with an arbitrary object.  This method returns <code>true</code> if and 
 436:      * only if:
 437:      * <ul>
 438:      * <li><code>obj</code> is not <code>null</code>;</li>
 439:      * <li><code>obj</code> is an instance of 
 440:      *         <code>DefaultIntervalXYDataset</code>;</li>
 441:      * <li>both datasets have the same number of series, each containing 
 442:      *         exactly the same values.</li>
 443:      * </ul>
 444:      * 
 445:      * @param obj  the object (<code>null</code> permitted).
 446:      * 
 447:      * @return A boolean.
 448:      */
 449:     public boolean equals(Object obj) {
 450:         if (obj == this) {
 451:             return true;
 452:         }
 453:         if (!(obj instanceof DefaultIntervalXYDataset)) {
 454:             return false;
 455:         }
 456:         DefaultIntervalXYDataset that = (DefaultIntervalXYDataset) obj;
 457:         if (!this.seriesKeys.equals(that.seriesKeys)) {
 458:             return false;
 459:         }
 460:         for (int i = 0; i < this.seriesList.size(); i++) {
 461:             double[][] d1 = (double[][]) this.seriesList.get(i);
 462:             double[][] d2 = (double[][]) that.seriesList.get(i);
 463:             double[] d1x = d1[0];
 464:             double[] d2x = d2[0];
 465:             if (!Arrays.equals(d1x, d2x)) {
 466:                 return false;
 467:             }
 468:             double[] d1xs = d1[1];
 469:             double[] d2xs = d2[1];
 470:             if (!Arrays.equals(d1xs, d2xs)) {
 471:                 return false;
 472:             }
 473:             double[] d1xe = d1[2];
 474:             double[] d2xe = d2[2];
 475:             if (!Arrays.equals(d1xe, d2xe)) {
 476:                 return false;
 477:             }
 478:             double[] d1y = d1[3];
 479:             double[] d2y = d2[3];            
 480:             if (!Arrays.equals(d1y, d2y)) {
 481:                 return false;
 482:             }
 483:             double[] d1ys = d1[4];
 484:             double[] d2ys = d2[4];            
 485:             if (!Arrays.equals(d1ys, d2ys)) {
 486:                 return false;
 487:             }
 488:             double[] d1ye = d1[5];
 489:             double[] d2ye = d2[5];            
 490:             if (!Arrays.equals(d1ye, d2ye)) {
 491:                 return false;
 492:             }
 493:         }
 494:         return true;
 495:     }
 496:     
 497:     /**
 498:      * Returns a hash code for this instance.
 499:      * 
 500:      * @return A hash code.
 501:      */
 502:     public int hashCode() {
 503:         int result;
 504:         result = this.seriesKeys.hashCode();
 505:         result = 29 * result + this.seriesList.hashCode();
 506:         return result;
 507:     }
 508:     
 509:     /**
 510:      * Returns a clone of this dataset.
 511:      * 
 512:      * @return A clone.
 513:      * 
 514:      * @throws CloneNotSupportedException if the dataset contains a series with
 515:      *         a key that cannot be cloned.
 516:      */
 517:     public Object clone() throws CloneNotSupportedException {
 518:         DefaultIntervalXYDataset clone 
 519:                 = (DefaultIntervalXYDataset) super.clone();
 520:         clone.seriesKeys = new java.util.ArrayList(this.seriesKeys);
 521:         clone.seriesList = new ArrayList(this.seriesList.size());
 522:         for (int i = 0; i < this.seriesList.size(); i++) {
 523:             double[][] data = (double[][]) this.seriesList.get(i);
 524:             double[] x = data[0];
 525:             double[] xStart = data[1];
 526:             double[] xEnd = data[2];
 527:             double[] y = data[3];
 528:             double[] yStart = data[4];
 529:             double[] yEnd = data[5];
 530:             double[] xx = new double[x.length];
 531:             double[] xxStart = new double[xStart.length];
 532:             double[] xxEnd = new double[xEnd.length];
 533:             double[] yy = new double[y.length];
 534:             double[] yyStart = new double[yStart.length];
 535:             double[] yyEnd = new double[yEnd.length];
 536:             System.arraycopy(x, 0, xx, 0, x.length);
 537:             System.arraycopy(xStart, 0, xxStart, 0, xStart.length);
 538:             System.arraycopy(xEnd, 0, xxEnd, 0, xEnd.length);
 539:             System.arraycopy(y, 0, yy, 0, y.length);
 540:             System.arraycopy(yStart, 0, yyStart, 0, yStart.length);
 541:             System.arraycopy(yEnd, 0, yyEnd, 0, yEnd.length);
 542:             clone.seriesList.add(i, new double[][] {xx, xxStart, xxEnd, yy, 
 543:                     yyStart, yyEnd});
 544:         }
 545:         return clone;
 546:     }
 547: 
 548: }