001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------------------- 028 * DefaultXYDataset.java 029 * --------------------- 030 * (C) Copyright 2006-2008, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 06-Jul-2006 : Version 1 (DG); 038 * 02-Nov-2006 : Fixed a problem with adding a new series with the same key 039 * as an existing series (see bug 1589392) (DG); 040 * 25-Jan-2007 : Implemented PublicCloneable (DG); 041 * 042 */ 043 044package org.jfree.data.xy; 045 046import java.util.ArrayList; 047import java.util.Arrays; 048import java.util.List; 049 050import org.jfree.data.DomainOrder; 051import org.jfree.data.general.DatasetChangeEvent; 052import org.jfree.util.PublicCloneable; 053 054/** 055 * A default implementation of the {@link XYDataset} interface that stores 056 * data values in arrays of double primitives. 057 * 058 * @since 1.0.2 059 */ 060public class DefaultXYDataset extends AbstractXYDataset 061 implements XYDataset, PublicCloneable { 062 063 /** 064 * Storage for the series keys. This list must be kept in sync with the 065 * seriesList. 066 */ 067 private List seriesKeys; 068 069 /** 070 * Storage for the series in the dataset. We use a list because the 071 * order of the series is significant. This list must be kept in sync 072 * with the seriesKeys list. 073 */ 074 private List seriesList; 075 076 /** 077 * Creates a new <code>DefaultXYDataset</code> instance, initially 078 * containing no data. 079 */ 080 public DefaultXYDataset() { 081 this.seriesKeys = new java.util.ArrayList(); 082 this.seriesList = new java.util.ArrayList(); 083 } 084 085 /** 086 * Returns the number of series in the dataset. 087 * 088 * @return The series count. 089 */ 090 @Override 091 public int getSeriesCount() { 092 return this.seriesList.size(); 093 } 094 095 /** 096 * Returns the key for a series. 097 * 098 * @param series the series index (in the range <code>0</code> to 099 * <code>getSeriesCount() - 1</code>). 100 * 101 * @return The key for the series. 102 * 103 * @throws IllegalArgumentException if <code>series</code> is not in the 104 * specified range. 105 */ 106 @Override 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 @Override 123 public int indexOf(Comparable seriesKey) { 124 return this.seriesKeys.indexOf(seriesKey); 125 } 126 127 /** 128 * Returns the order of the domain (x-) values in the dataset. In this 129 * implementation, we cannot guarantee that the x-values are ordered, so 130 * this method returns <code>DomainOrder.NONE</code>. 131 * 132 * @return <code>DomainOrder.NONE</code>. 133 */ 134 @Override 135 public DomainOrder getDomainOrder() { 136 return DomainOrder.NONE; 137 } 138 139 /** 140 * Returns the number of items in the specified series. 141 * 142 * @param series the series index (in the range <code>0</code> to 143 * <code>getSeriesCount() - 1</code>). 144 * 145 * @return The item count. 146 * 147 * @throws IllegalArgumentException if <code>series</code> is not in the 148 * specified range. 149 */ 150 @Override 151 public int getItemCount(int series) { 152 if ((series < 0) || (series >= getSeriesCount())) { 153 throw new IllegalArgumentException("Series index out of bounds"); 154 } 155 double[][] seriesArray = (double[][]) this.seriesList.get(series); 156 return seriesArray[0].length; 157 } 158 159 /** 160 * Returns the x-value for an item within a series. 161 * 162 * @param series the series index (in the range <code>0</code> to 163 * <code>getSeriesCount() - 1</code>). 164 * @param item the item index (in the range <code>0</code> to 165 * <code>getItemCount(series)</code>). 166 * 167 * @return The x-value. 168 * 169 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 170 * within the specified range. 171 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 172 * within the specified range. 173 * 174 * @see #getX(int, int) 175 */ 176 @Override 177 public double getXValue(int series, int item) { 178 double[][] seriesData = (double[][]) this.seriesList.get(series); 179 return seriesData[0][item]; 180 } 181 182 /** 183 * Returns the x-value for an item within a series. 184 * 185 * @param series the series index (in the range <code>0</code> to 186 * <code>getSeriesCount() - 1</code>). 187 * @param item the item index (in the range <code>0</code> to 188 * <code>getItemCount(series)</code>). 189 * 190 * @return The x-value. 191 * 192 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 193 * within the specified range. 194 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 195 * within the specified range. 196 * 197 * @see #getXValue(int, int) 198 */ 199 @Override 200 public Number getX(int series, int item) { 201 return new Double(getXValue(series, item)); 202 } 203 204 /** 205 * Returns the y-value for an item within a series. 206 * 207 * @param series the series index (in the range <code>0</code> to 208 * <code>getSeriesCount() - 1</code>). 209 * @param item the item index (in the range <code>0</code> to 210 * <code>getItemCount(series)</code>). 211 * 212 * @return The y-value. 213 * 214 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 215 * within the specified range. 216 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 217 * within the specified range. 218 * 219 * @see #getY(int, int) 220 */ 221 @Override 222 public double getYValue(int series, int item) { 223 double[][] seriesData = (double[][]) this.seriesList.get(series); 224 return seriesData[1][item]; 225 } 226 227 /** 228 * Returns the y-value for an item within a series. 229 * 230 * @param series the series index (in the range <code>0</code> to 231 * <code>getSeriesCount() - 1</code>). 232 * @param item the item index (in the range <code>0</code> to 233 * <code>getItemCount(series)</code>). 234 * 235 * @return The y-value. 236 * 237 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 238 * within the specified range. 239 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 240 * within the specified range. 241 * 242 * @see #getX(int, int) 243 */ 244 @Override 245 public Number getY(int series, int item) { 246 return new Double(getYValue(series, item)); 247 } 248 249 /** 250 * Adds a series or if a series with the same key already exists replaces 251 * the data for that series, then sends a {@link DatasetChangeEvent} to 252 * all registered listeners. 253 * 254 * @param seriesKey the series key (<code>null</code> not permitted). 255 * @param data the data (must be an array with length 2, containing two 256 * arrays of equal length, the first containing the x-values and the 257 * second containing the y-values). 258 */ 259 public void addSeries(Comparable seriesKey, double[][] data) { 260 if (seriesKey == null) { 261 throw new IllegalArgumentException( 262 "The 'seriesKey' cannot be null."); 263 } 264 if (data == null) { 265 throw new IllegalArgumentException("The 'data' is null."); 266 } 267 if (data.length != 2) { 268 throw new IllegalArgumentException( 269 "The 'data' array must have length == 2."); 270 } 271 if (data[0].length != data[1].length) { 272 throw new IllegalArgumentException( 273 "The 'data' array must contain two arrays with equal length."); 274 } 275 int seriesIndex = indexOf(seriesKey); 276 if (seriesIndex == -1) { // add a new series 277 this.seriesKeys.add(seriesKey); 278 this.seriesList.add(data); 279 } 280 else { // replace an existing series 281 this.seriesList.remove(seriesIndex); 282 this.seriesList.add(seriesIndex, data); 283 } 284 notifyListeners(new DatasetChangeEvent(this, this)); 285 } 286 287 /** 288 * Removes a series from the dataset, then sends a 289 * {@link DatasetChangeEvent} to all registered listeners. 290 * 291 * @param seriesKey the series key (<code>null</code> not permitted). 292 * 293 */ 294 public void removeSeries(Comparable seriesKey) { 295 int seriesIndex = indexOf(seriesKey); 296 if (seriesIndex >= 0) { 297 this.seriesKeys.remove(seriesIndex); 298 this.seriesList.remove(seriesIndex); 299 notifyListeners(new DatasetChangeEvent(this, this)); 300 } 301 } 302 303 /** 304 * Tests this <code>DefaultXYDataset</code> instance for equality with an 305 * arbitrary object. This method returns <code>true</code> if and only if: 306 * <ul> 307 * <li><code>obj</code> is not <code>null</code>;</li> 308 * <li><code>obj</code> is an instance of 309 * <code>DefaultXYDataset</code>;</li> 310 * <li>both datasets have the same number of series, each containing 311 * exactly the same values.</li> 312 * </ul> 313 * 314 * @param obj the object (<code>null</code> permitted). 315 * 316 * @return A boolean. 317 */ 318 @Override 319 public boolean equals(Object obj) { 320 if (obj == this) { 321 return true; 322 } 323 if (!(obj instanceof DefaultXYDataset)) { 324 return false; 325 } 326 DefaultXYDataset that = (DefaultXYDataset) obj; 327 if (!this.seriesKeys.equals(that.seriesKeys)) { 328 return false; 329 } 330 for (int i = 0; i < this.seriesList.size(); i++) { 331 double[][] d1 = (double[][]) this.seriesList.get(i); 332 double[][] d2 = (double[][]) that.seriesList.get(i); 333 double[] d1x = d1[0]; 334 double[] d2x = d2[0]; 335 if (!Arrays.equals(d1x, d2x)) { 336 return false; 337 } 338 double[] d1y = d1[1]; 339 double[] d2y = d2[1]; 340 if (!Arrays.equals(d1y, d2y)) { 341 return false; 342 } 343 } 344 return true; 345 } 346 347 /** 348 * Returns a hash code for this instance. 349 * 350 * @return A hash code. 351 */ 352 @Override 353 public int hashCode() { 354 int result; 355 result = this.seriesKeys.hashCode(); 356 result = 29 * result + this.seriesList.hashCode(); 357 return result; 358 } 359 360 /** 361 * Creates an independent copy of this dataset. 362 * 363 * @return The cloned dataset. 364 * 365 * @throws CloneNotSupportedException if there is a problem cloning the 366 * dataset (for instance, if a non-cloneable object is used for a 367 * series key). 368 */ 369 @Override 370 public Object clone() throws CloneNotSupportedException { 371 DefaultXYDataset clone = (DefaultXYDataset) super.clone(); 372 clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); 373 clone.seriesList = new ArrayList(this.seriesList.size()); 374 for (int i = 0; i < this.seriesList.size(); i++) { 375 double[][] data = (double[][]) this.seriesList.get(i); 376 double[] x = data[0]; 377 double[] y = data[1]; 378 double[] xx = new double[x.length]; 379 double[] yy = new double[y.length]; 380 System.arraycopy(x, 0, xx, 0, x.length); 381 System.arraycopy(y, 0, yy, 0, y.length); 382 clone.seriesList.add(i, new double[][] {xx, yy}); 383 } 384 return clone; 385 } 386 387}