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