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