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