Frames | No Frames |
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: }