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