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: * DefaultXYZDataset.java 29: * ---------------------- 30: * (C) Copyright 2006, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: DefaultXYZDataset.java,v 1.1.2.4 2006/11/28 14:06:14 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 12-Jul-2006 : Version 1 (DG); 40: * 06-Oct-2006 : Fixed API doc warnings (DG); 41: * 02-Nov-2006 : Fixed a problem with adding a new series with the same key 42: * as an existing series (see bug 1589392) (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.DomainOrder; 53: import org.jfree.data.general.DatasetChangeEvent; 54: 55: /** 56: * A default implementation of the {@link XYZDataset} interface that stores 57: * data values in arrays of double primitives. 58: * 59: * @since 1.0.2 60: */ 61: public class DefaultXYZDataset extends AbstractXYZDataset 62: implements XYZDataset { 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>DefaultXYZDataset</code> instance, initially 79: * containing no data. 80: */ 81: public DefaultXYZDataset() { 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 index of the series with the specified key, or -1 if there 115: * is no such series in the dataset. 116: * 117: * @param seriesKey the series key (<code>null</code> permitted). 118: * 119: * @return The index, or -1. 120: */ 121: public int indexOf(Comparable seriesKey) { 122: return this.seriesKeys.indexOf(seriesKey); 123: } 124: 125: /** 126: * Returns the order of the domain (x-) values in the dataset. In this 127: * implementation, we cannot guarantee that the x-values are ordered, so 128: * this method returns <code>DomainOrder.NONE</code>. 129: * 130: * @return <code>DomainOrder.NONE</code>. 131: */ 132: public DomainOrder getDomainOrder() { 133: return DomainOrder.NONE; 134: } 135: 136: /** 137: * Returns the number of items in the specified series. 138: * 139: * @param series the series index (in the range <code>0</code> to 140: * <code>getSeriesCount() - 1</code>). 141: * 142: * @return The item count. 143: * 144: * @throws IllegalArgumentException if <code>series</code> is not in the 145: * specified range. 146: */ 147: public int getItemCount(int series) { 148: if ((series < 0) || (series >= getSeriesCount())) { 149: throw new IllegalArgumentException("Series index out of bounds"); 150: } 151: double[][] seriesArray = (double[][]) this.seriesList.get(series); 152: return seriesArray[0].length; 153: } 154: 155: /** 156: * Returns the x-value for an item within a series. 157: * 158: * @param series the series index (in the range <code>0</code> to 159: * <code>getSeriesCount() - 1</code>). 160: * @param item the item index (in the range <code>0</code> to 161: * <code>getItemCount(series)</code>). 162: * 163: * @return The x-value. 164: * 165: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 166: * within the specified range. 167: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 168: * within the specified range. 169: * 170: * @see #getX(int, int) 171: */ 172: public double getXValue(int series, int item) { 173: double[][] seriesData = (double[][]) this.seriesList.get(series); 174: return seriesData[0][item]; 175: } 176: 177: /** 178: * Returns the x-value for an item within a series. 179: * 180: * @param series the series index (in the range <code>0</code> to 181: * <code>getSeriesCount() - 1</code>). 182: * @param item the item index (in the range <code>0</code> to 183: * <code>getItemCount(series)</code>). 184: * 185: * @return The x-value. 186: * 187: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 188: * within the specified range. 189: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 190: * within the specified range. 191: * 192: * @see #getXValue(int, int) 193: */ 194: public Number getX(int series, int item) { 195: return new Double(getXValue(series, item)); 196: } 197: 198: /** 199: * Returns the y-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 y-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 #getY(int, int) 214: */ 215: public double getYValue(int series, int item) { 216: double[][] seriesData = (double[][]) this.seriesList.get(series); 217: return seriesData[1][item]; 218: } 219: 220: /** 221: * Returns the 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 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 #getX(int, int) 236: */ 237: public Number getY(int series, int item) { 238: return new Double(getYValue(series, item)); 239: } 240: 241: /** 242: * Returns the z-value for an item within a series. 243: * 244: * @param series the series index (in the range <code>0</code> to 245: * <code>getSeriesCount() - 1</code>). 246: * @param item the item index (in the range <code>0</code> to 247: * <code>getItemCount(series)</code>). 248: * 249: * @return The z-value. 250: * 251: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 252: * within the specified range. 253: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 254: * within the specified range. 255: * 256: * @see #getZ(int, int) 257: */ 258: public double getZValue(int series, int item) { 259: double[][] seriesData = (double[][]) this.seriesList.get(series); 260: return seriesData[2][item]; 261: } 262: 263: /** 264: * Returns the z-value for an item within a series. 265: * 266: * @param series the series index (in the range <code>0</code> to 267: * <code>getSeriesCount() - 1</code>). 268: * @param item the item index (in the range <code>0</code> to 269: * <code>getItemCount(series)</code>). 270: * 271: * @return The z-value. 272: * 273: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 274: * within the specified range. 275: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 276: * within the specified range. 277: * 278: * @see #getZ(int, int) 279: */ 280: public Number getZ(int series, int item) { 281: return new Double(getZValue(series, item)); 282: } 283: 284: /** 285: * Adds a series or if a series with the same key already exists replaces 286: * the data for that series, then sends a {@link DatasetChangeEvent} to 287: * all registered listeners. 288: * 289: * @param seriesKey the series key (<code>null</code> not permitted). 290: * @param data the data (must be an array with length 3, containing three 291: * arrays of equal length, the first containing the x-values, the 292: * second containing the y-values and the third containing the 293: * z-values). 294: */ 295: public void addSeries(Comparable seriesKey, double[][] data) { 296: if (seriesKey == null) { 297: throw new IllegalArgumentException( 298: "The 'seriesKey' cannot be null."); 299: } 300: if (data == null) { 301: throw new IllegalArgumentException("The 'data' is null."); 302: } 303: if (data.length != 3) { 304: throw new IllegalArgumentException( 305: "The 'data' array must have length == 3."); 306: } 307: if (data[0].length != data[1].length 308: || data[0].length != data[2].length) { 309: throw new IllegalArgumentException("The 'data' array must contain " 310: + "three arrays all having the same length."); 311: } 312: int seriesIndex = indexOf(seriesKey); 313: if (seriesIndex == -1) { // add a new series 314: this.seriesKeys.add(seriesKey); 315: this.seriesList.add(data); 316: } 317: else { // replace an existing series 318: this.seriesList.remove(seriesIndex); 319: this.seriesList.add(seriesIndex, data); 320: } 321: notifyListeners(new DatasetChangeEvent(this, this)); 322: } 323: 324: /** 325: * Removes a series from the dataset, then sends a 326: * {@link DatasetChangeEvent} to all registered listeners. 327: * 328: * @param seriesKey the series key (<code>null</code> not permitted). 329: * 330: */ 331: public void removeSeries(Comparable seriesKey) { 332: int seriesIndex = indexOf(seriesKey); 333: if (seriesIndex >= 0) { 334: this.seriesKeys.remove(seriesIndex); 335: this.seriesList.remove(seriesIndex); 336: notifyListeners(new DatasetChangeEvent(this, this)); 337: } 338: } 339: 340: /** 341: * Tests this <code>DefaultXYDataset</code> instance for equality with an 342: * arbitrary object. This method returns <code>true</code> if and only if: 343: * <ul> 344: * <li><code>obj</code> is not <code>null</code>;</li> 345: * <li><code>obj</code> is an instance of 346: * <code>DefaultXYDataset</code>;</li> 347: * <li>both datasets have the same number of series, each containing 348: * exactly the same values.</li> 349: * </ul> 350: * 351: * @param obj the object (<code>null</code> permitted). 352: * 353: * @return A boolean. 354: */ 355: public boolean equals(Object obj) { 356: if (obj == this) { 357: return true; 358: } 359: if (!(obj instanceof DefaultXYZDataset)) { 360: return false; 361: } 362: DefaultXYZDataset that = (DefaultXYZDataset) obj; 363: if (!this.seriesKeys.equals(that.seriesKeys)) { 364: return false; 365: } 366: for (int i = 0; i < this.seriesList.size(); i++) { 367: double[][] d1 = (double[][]) this.seriesList.get(i); 368: double[][] d2 = (double[][]) that.seriesList.get(i); 369: double[] d1x = d1[0]; 370: double[] d2x = d2[0]; 371: if (!Arrays.equals(d1x, d2x)) { 372: return false; 373: } 374: double[] d1y = d1[1]; 375: double[] d2y = d2[1]; 376: if (!Arrays.equals(d1y, d2y)) { 377: return false; 378: } 379: double[] d1z = d1[2]; 380: double[] d2z = d2[2]; 381: if (!Arrays.equals(d1z, d2z)) { 382: return false; 383: } 384: } 385: return true; 386: } 387: 388: /** 389: * Returns a hash code for this instance. 390: * 391: * @return A hash code. 392: */ 393: public int hashCode() { 394: int result; 395: result = this.seriesKeys.hashCode(); 396: result = 29 * result + this.seriesList.hashCode(); 397: return result; 398: } 399: 400: /** 401: * Creates an independent copy of this dataset. 402: * 403: * @return The cloned dataset. 404: * 405: * @throws CloneNotSupportedException if there is a problem cloning the 406: * dataset (for instance, if a non-cloneable object is used for a 407: * series key). 408: */ 409: public Object clone() throws CloneNotSupportedException { 410: DefaultXYZDataset clone = (DefaultXYZDataset) super.clone(); 411: clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); 412: clone.seriesList = new ArrayList(this.seriesList.size()); 413: for (int i = 0; i < this.seriesList.size(); i++) { 414: double[][] data = (double[][]) this.seriesList.get(i); 415: double[] x = data[0]; 416: double[] y = data[1]; 417: double[] z = data[2]; 418: double[] xx = new double[x.length]; 419: double[] yy = new double[y.length]; 420: double[] zz = new double[z.length]; 421: System.arraycopy(x, 0, xx, 0, x.length); 422: System.arraycopy(y, 0, yy, 0, y.length); 423: System.arraycopy(z, 0, zz, 0, z.length); 424: clone.seriesList.add(i, new double[][] {xx, yy, zz}); 425: } 426: return clone; 427: } 428: 429: }