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