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