001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2006, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ----------------------------- 028 * DefaultIntervalXYDataset.java 029 * ----------------------------- 030 * (C) Copyright 2006, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: DefaultIntervalXYDataset.java,v 1.1.2.3 2006/11/28 14:06:14 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 23-Oct-2006 : Version 1 (DG); 040 * 02-Nov-2006 : Fixed a problem with adding a new series with the same key 041 * as an existing series (see bug 1589392) (DG); 042 * 28-Nov-2006 : New override for clone() (DG); 043 * 044 */ 045 046 package org.jfree.data.xy; 047 048 import java.util.ArrayList; 049 import java.util.Arrays; 050 import java.util.List; 051 052 import org.jfree.data.general.DatasetChangeEvent; 053 054 /** 055 * A dataset that defines a range (interval) for both the x-values and the 056 * y-values. This implementation uses six arrays to store the x, start-x, 057 * end-x, y, start-y and end-y values. 058 * <br><br> 059 * An alternative implementation of the {@link IntervalXYDataset} interface 060 * is provided by the {@link XYIntervalSeriesCollection} class. 061 * 062 * @since 1.0.3 063 */ 064 public class DefaultIntervalXYDataset extends AbstractIntervalXYDataset { 065 066 /** 067 * Storage for the series keys. This list must be kept in sync with the 068 * seriesList. 069 */ 070 private List seriesKeys; 071 072 /** 073 * Storage for the series in the dataset. We use a list because the 074 * order of the series is significant. This list must be kept in sync 075 * with the seriesKeys list. 076 */ 077 private List seriesList; 078 079 /** 080 * Creates a new <code>DefaultIntervalXYDataset</code> instance, initially 081 * containing no data. 082 */ 083 public DefaultIntervalXYDataset() { 084 this.seriesKeys = new java.util.ArrayList(); 085 this.seriesList = new java.util.ArrayList(); 086 } 087 088 /** 089 * Returns the number of series in the dataset. 090 * 091 * @return The series count. 092 */ 093 public int getSeriesCount() { 094 return this.seriesList.size(); 095 } 096 097 /** 098 * Returns the key for a series. 099 * 100 * @param series the series index (in the range <code>0</code> to 101 * <code>getSeriesCount() - 1</code>). 102 * 103 * @return The key for the series. 104 * 105 * @throws IllegalArgumentException if <code>series</code> is not in the 106 * specified range. 107 */ 108 public Comparable getSeriesKey(int series) { 109 if ((series < 0) || (series >= getSeriesCount())) { 110 throw new IllegalArgumentException("Series index out of bounds"); 111 } 112 return (Comparable) this.seriesKeys.get(series); 113 } 114 115 /** 116 * Returns the number of items in the specified series. 117 * 118 * @param series the series index (in the range <code>0</code> to 119 * <code>getSeriesCount() - 1</code>). 120 * 121 * @return The item count. 122 * 123 * @throws IllegalArgumentException if <code>series</code> is not in the 124 * specified range. 125 */ 126 public int getItemCount(int series) { 127 if ((series < 0) || (series >= getSeriesCount())) { 128 throw new IllegalArgumentException("Series index out of bounds"); 129 } 130 double[][] seriesArray = (double[][]) this.seriesList.get(series); 131 return seriesArray[0].length; 132 } 133 134 /** 135 * Returns the x-value for an item within a series. 136 * 137 * @param series the series index (in the range <code>0</code> to 138 * <code>getSeriesCount() - 1</code>). 139 * @param item the item index (in the range <code>0</code> to 140 * <code>getItemCount(series)</code>). 141 * 142 * @return The x-value. 143 * 144 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 145 * within the specified range. 146 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 147 * within the specified range. 148 * 149 * @see #getX(int, int) 150 */ 151 public double getXValue(int series, int item) { 152 double[][] seriesData = (double[][]) this.seriesList.get(series); 153 return seriesData[0][item]; 154 } 155 156 /** 157 * Returns the y-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 y-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 #getY(int, int) 172 */ 173 public double getYValue(int series, int item) { 174 double[][] seriesData = (double[][]) this.seriesList.get(series); 175 return seriesData[3][item]; 176 } 177 178 /** 179 * Returns the starting 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 starting 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 #getStartX(int, int) 194 */ 195 public double getStartXValue(int series, int item) { 196 double[][] seriesData = (double[][]) this.seriesList.get(series); 197 return seriesData[1][item]; 198 } 199 200 /** 201 * Returns the ending x-value for an item within a series. 202 * 203 * @param series the series index (in the range <code>0</code> to 204 * <code>getSeriesCount() - 1</code>). 205 * @param item the item index (in the range <code>0</code> to 206 * <code>getItemCount(series)</code>). 207 * 208 * @return The ending x-value. 209 * 210 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 211 * within the specified range. 212 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 213 * within the specified range. 214 * 215 * @see #getEndX(int, int) 216 */ 217 public double getEndXValue(int series, int item) { 218 double[][] seriesData = (double[][]) this.seriesList.get(series); 219 return seriesData[2][item]; 220 } 221 222 /** 223 * Returns the starting y-value for an item within a series. 224 * 225 * @param series the series index (in the range <code>0</code> to 226 * <code>getSeriesCount() - 1</code>). 227 * @param item the item index (in the range <code>0</code> to 228 * <code>getItemCount(series)</code>). 229 * 230 * @return The starting y-value. 231 * 232 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 233 * within the specified range. 234 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 235 * within the specified range. 236 * 237 * @see #getStartY(int, int) 238 */ 239 public double getStartYValue(int series, int item) { 240 double[][] seriesData = (double[][]) this.seriesList.get(series); 241 return seriesData[4][item]; 242 } 243 244 /** 245 * Returns the ending y-value for an item within a series. 246 * 247 * @param series the series index (in the range <code>0</code> to 248 * <code>getSeriesCount() - 1</code>). 249 * @param item the item index (in the range <code>0</code> to 250 * <code>getItemCount(series)</code>). 251 * 252 * @return The ending y-value. 253 * 254 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 255 * within the specified range. 256 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 257 * within the specified range. 258 * 259 * @see #getEndY(int, int) 260 */ 261 public double getEndYValue(int series, int item) { 262 double[][] seriesData = (double[][]) this.seriesList.get(series); 263 return seriesData[5][item]; 264 } 265 266 /** 267 * Returns the ending x-value for an item within a series. 268 * 269 * @param series the series index (in the range <code>0</code> to 270 * <code>getSeriesCount() - 1</code>). 271 * @param item the item index (in the range <code>0</code> to 272 * <code>getItemCount(series)</code>). 273 * 274 * @return The ending x-value. 275 * 276 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 277 * within the specified range. 278 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 279 * within the specified range. 280 * 281 * @see #getEndXValue(int, int) 282 */ 283 public Number getEndX(int series, int item) { 284 return new Double(getEndXValue(series, item)); 285 } 286 287 /** 288 * Returns the ending y-value for an item within a series. 289 * 290 * @param series the series index (in the range <code>0</code> to 291 * <code>getSeriesCount() - 1</code>). 292 * @param item the item index (in the range <code>0</code> to 293 * <code>getItemCount(series)</code>). 294 * 295 * @return The ending y-value. 296 * 297 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 298 * within the specified range. 299 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 300 * within the specified range. 301 * 302 * @see #getEndYValue(int, int) 303 */ 304 public Number getEndY(int series, int item) { 305 return new Double(getEndYValue(series, item)); 306 } 307 308 /** 309 * Returns the starting x-value for an item within a series. 310 * 311 * @param series the series index (in the range <code>0</code> to 312 * <code>getSeriesCount() - 1</code>). 313 * @param item the item index (in the range <code>0</code> to 314 * <code>getItemCount(series)</code>). 315 * 316 * @return The starting x-value. 317 * 318 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 319 * within the specified range. 320 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 321 * within the specified range. 322 * 323 * @see #getStartXValue(int, int) 324 */ 325 public Number getStartX(int series, int item) { 326 return new Double(getStartXValue(series, item)); 327 } 328 329 /** 330 * Returns the starting y-value for an item within a series. 331 * 332 * @param series the series index (in the range <code>0</code> to 333 * <code>getSeriesCount() - 1</code>). 334 * @param item the item index (in the range <code>0</code> to 335 * <code>getItemCount(series)</code>). 336 * 337 * @return The starting y-value. 338 * 339 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 340 * within the specified range. 341 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 342 * within the specified range. 343 * 344 * @see #getStartYValue(int, int) 345 */ 346 public Number getStartY(int series, int item) { 347 return new Double(getStartYValue(series, item)); 348 } 349 350 /** 351 * Returns the x-value for an item within a series. 352 * 353 * @param series the series index (in the range <code>0</code> to 354 * <code>getSeriesCount() - 1</code>). 355 * @param item the item index (in the range <code>0</code> to 356 * <code>getItemCount(series)</code>). 357 * 358 * @return The x-value. 359 * 360 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 361 * within the specified range. 362 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 363 * within the specified range. 364 * 365 * @see #getXValue(int, int) 366 */ 367 public Number getX(int series, int item) { 368 return new Double(getXValue(series, item)); 369 } 370 371 /** 372 * Returns the y-value for an item within a series. 373 * 374 * @param series the series index (in the range <code>0</code> to 375 * <code>getSeriesCount() - 1</code>). 376 * @param item the item index (in the range <code>0</code> to 377 * <code>getItemCount(series)</code>). 378 * 379 * @return The y-value. 380 * 381 * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not 382 * within the specified range. 383 * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not 384 * within the specified range. 385 * 386 * @see #getYValue(int, int) 387 */ 388 public Number getY(int series, int item) { 389 return new Double(getYValue(series, item)); 390 } 391 392 /** 393 * Adds a series or if a series with the same key already exists replaces 394 * the data for that series, then sends a {@link DatasetChangeEvent} to 395 * all registered listeners. 396 * 397 * @param seriesKey the series key (<code>null</code> not permitted). 398 * @param data the data (must be an array with length 6, containing six 399 * arrays of equal length, the first containing the x-values and the 400 * second containing the y-values). 401 */ 402 public void addSeries(Comparable seriesKey, double[][] data) { 403 if (seriesKey == null) { 404 throw new IllegalArgumentException( 405 "The 'seriesKey' cannot be null."); 406 } 407 if (data == null) { 408 throw new IllegalArgumentException("The 'data' is null."); 409 } 410 if (data.length != 6) { 411 throw new IllegalArgumentException( 412 "The 'data' array must have length == 6."); 413 } 414 int length = data[0].length; 415 if (length != data[1].length || length != data[2].length 416 || length != data[3].length || length != data[4].length 417 || length != data[5].length) { 418 throw new IllegalArgumentException( 419 "The 'data' array must contain two arrays with equal length."); 420 } 421 int seriesIndex = indexOf(seriesKey); 422 if (seriesIndex == -1) { // add a new series 423 this.seriesKeys.add(seriesKey); 424 this.seriesList.add(data); 425 } 426 else { // replace an existing series 427 this.seriesList.remove(seriesIndex); 428 this.seriesList.add(seriesIndex, data); 429 } 430 notifyListeners(new DatasetChangeEvent(this, this)); 431 } 432 433 /** 434 * Tests this <code>DefaultIntervalXYDataset</code> instance for equality 435 * with an arbitrary object. This method returns <code>true</code> if and 436 * only if: 437 * <ul> 438 * <li><code>obj</code> is not <code>null</code>;</li> 439 * <li><code>obj</code> is an instance of 440 * <code>DefaultIntervalXYDataset</code>;</li> 441 * <li>both datasets have the same number of series, each containing 442 * exactly the same values.</li> 443 * </ul> 444 * 445 * @param obj the object (<code>null</code> permitted). 446 * 447 * @return A boolean. 448 */ 449 public boolean equals(Object obj) { 450 if (obj == this) { 451 return true; 452 } 453 if (!(obj instanceof DefaultIntervalXYDataset)) { 454 return false; 455 } 456 DefaultIntervalXYDataset that = (DefaultIntervalXYDataset) obj; 457 if (!this.seriesKeys.equals(that.seriesKeys)) { 458 return false; 459 } 460 for (int i = 0; i < this.seriesList.size(); i++) { 461 double[][] d1 = (double[][]) this.seriesList.get(i); 462 double[][] d2 = (double[][]) that.seriesList.get(i); 463 double[] d1x = d1[0]; 464 double[] d2x = d2[0]; 465 if (!Arrays.equals(d1x, d2x)) { 466 return false; 467 } 468 double[] d1xs = d1[1]; 469 double[] d2xs = d2[1]; 470 if (!Arrays.equals(d1xs, d2xs)) { 471 return false; 472 } 473 double[] d1xe = d1[2]; 474 double[] d2xe = d2[2]; 475 if (!Arrays.equals(d1xe, d2xe)) { 476 return false; 477 } 478 double[] d1y = d1[3]; 479 double[] d2y = d2[3]; 480 if (!Arrays.equals(d1y, d2y)) { 481 return false; 482 } 483 double[] d1ys = d1[4]; 484 double[] d2ys = d2[4]; 485 if (!Arrays.equals(d1ys, d2ys)) { 486 return false; 487 } 488 double[] d1ye = d1[5]; 489 double[] d2ye = d2[5]; 490 if (!Arrays.equals(d1ye, d2ye)) { 491 return false; 492 } 493 } 494 return true; 495 } 496 497 /** 498 * Returns a hash code for this instance. 499 * 500 * @return A hash code. 501 */ 502 public int hashCode() { 503 int result; 504 result = this.seriesKeys.hashCode(); 505 result = 29 * result + this.seriesList.hashCode(); 506 return result; 507 } 508 509 /** 510 * Returns a clone of this dataset. 511 * 512 * @return A clone. 513 * 514 * @throws CloneNotSupportedException if the dataset contains a series with 515 * a key that cannot be cloned. 516 */ 517 public Object clone() throws CloneNotSupportedException { 518 DefaultIntervalXYDataset clone 519 = (DefaultIntervalXYDataset) super.clone(); 520 clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); 521 clone.seriesList = new ArrayList(this.seriesList.size()); 522 for (int i = 0; i < this.seriesList.size(); i++) { 523 double[][] data = (double[][]) this.seriesList.get(i); 524 double[] x = data[0]; 525 double[] xStart = data[1]; 526 double[] xEnd = data[2]; 527 double[] y = data[3]; 528 double[] yStart = data[4]; 529 double[] yEnd = data[5]; 530 double[] xx = new double[x.length]; 531 double[] xxStart = new double[xStart.length]; 532 double[] xxEnd = new double[xEnd.length]; 533 double[] yy = new double[y.length]; 534 double[] yyStart = new double[yStart.length]; 535 double[] yyEnd = new double[yEnd.length]; 536 System.arraycopy(x, 0, xx, 0, x.length); 537 System.arraycopy(xStart, 0, xxStart, 0, xStart.length); 538 System.arraycopy(xEnd, 0, xxEnd, 0, xEnd.length); 539 System.arraycopy(y, 0, yy, 0, y.length); 540 System.arraycopy(yStart, 0, yyStart, 0, yStart.length); 541 System.arraycopy(yEnd, 0, yyEnd, 0, yEnd.length); 542 clone.seriesList.add(i, new double[][] {xx, xxStart, xxEnd, yy, 543 yyStart, yyEnd}); 544 } 545 return clone; 546 } 547 548 }