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