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: * XYSeriesCollection.java 29: * ----------------------- 30: * (C) Copyright 2001-2006, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): Aaron Metzger; 34: * 35: * $Id: XYSeriesCollection.java,v 1.12.2.5 2007/03/08 13:57:09 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 15-Nov-2001 : Version 1 (DG); 40: * 03-Apr-2002 : Added change listener code (DG); 41: * 29-Apr-2002 : Added removeSeries, removeAllSeries methods (ARM); 42: * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 43: * 26-Mar-2003 : Implemented Serializable (DG); 44: * 04-Aug-2003 : Added getSeries() method (DG); 45: * 31-Mar-2004 : Modified to use an XYIntervalDelegate. 46: * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG); 47: * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG); 48: * 17-Nov-2004 : Updated for changes to DomainInfo interface (DG); 49: * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG); 50: * 28-Mar-2005 : Fixed bug in getSeries(int) method (1170825) (DG); 51: * 05-Oct-2005 : Made the interval delegate a dataset listener (DG); 52: * ------------- JFREECHART 1.0.x --------------------------------------------- 53: * 27-Nov-2006 : Added clone() override (DG); 54: * 55: */ 56: 57: package org.jfree.data.xy; 58: 59: import java.io.Serializable; 60: import java.util.Collections; 61: import java.util.List; 62: 63: import org.jfree.data.DomainInfo; 64: import org.jfree.data.Range; 65: import org.jfree.data.general.DatasetChangeEvent; 66: import org.jfree.data.general.DatasetUtilities; 67: import org.jfree.util.ObjectUtilities; 68: 69: /** 70: * Represents a collection of {@link XYSeries} objects that can be used as a 71: * dataset. 72: */ 73: public class XYSeriesCollection extends AbstractIntervalXYDataset 74: implements IntervalXYDataset, DomainInfo, 75: Serializable { 76: 77: /** For serialization. */ 78: private static final long serialVersionUID = -7590013825931496766L; 79: 80: /** The series that are included in the collection. */ 81: private List data; 82: 83: /** The interval delegate (used to calculate the start and end x-values). */ 84: private IntervalXYDelegate intervalDelegate; 85: 86: /** 87: * Constructs an empty dataset. 88: */ 89: public XYSeriesCollection() { 90: this(null); 91: } 92: 93: /** 94: * Constructs a dataset and populates it with a single series. 95: * 96: * @param series the series (<code>null</code> ignored). 97: */ 98: public XYSeriesCollection(XYSeries series) { 99: this.data = new java.util.ArrayList(); 100: this.intervalDelegate = new IntervalXYDelegate(this, false); 101: addChangeListener(this.intervalDelegate); 102: if (series != null) { 103: this.data.add(series); 104: series.addChangeListener(this); 105: } 106: } 107: 108: /** 109: * Adds a series to the collection and sends a {@link DatasetChangeEvent} 110: * to all registered listeners. 111: * 112: * @param series the series (<code>null</code> not permitted). 113: */ 114: public void addSeries(XYSeries series) { 115: 116: if (series == null) { 117: throw new IllegalArgumentException("Null 'series' argument."); 118: } 119: this.data.add(series); 120: series.addChangeListener(this); 121: fireDatasetChanged(); 122: 123: } 124: 125: /** 126: * Removes a series from the collection and sends a 127: * {@link DatasetChangeEvent} to all registered listeners. 128: * 129: * @param series the series index (zero-based). 130: */ 131: public void removeSeries(int series) { 132: 133: if ((series < 0) || (series >= getSeriesCount())) { 134: throw new IllegalArgumentException("Series index out of bounds."); 135: } 136: 137: // fetch the series, remove the change listener, then remove the series. 138: XYSeries ts = (XYSeries) this.data.get(series); 139: ts.removeChangeListener(this); 140: this.data.remove(series); 141: fireDatasetChanged(); 142: 143: } 144: 145: /** 146: * Removes a series from the collection and sends a 147: * {@link DatasetChangeEvent} to all registered listeners. 148: * 149: * @param series the series (<code>null</code> not permitted). 150: */ 151: public void removeSeries(XYSeries series) { 152: 153: if (series == null) { 154: throw new IllegalArgumentException("Null 'series' argument."); 155: } 156: if (this.data.contains(series)) { 157: series.removeChangeListener(this); 158: this.data.remove(series); 159: fireDatasetChanged(); 160: } 161: 162: } 163: 164: /** 165: * Removes all the series from the collection and sends a 166: * {@link DatasetChangeEvent} to all registered listeners. 167: */ 168: public void removeAllSeries() { 169: // Unregister the collection as a change listener to each series in 170: // the collection. 171: for (int i = 0; i < this.data.size(); i++) { 172: XYSeries series = (XYSeries) this.data.get(i); 173: series.removeChangeListener(this); 174: } 175: 176: // Remove all the series from the collection and notify listeners. 177: this.data.clear(); 178: fireDatasetChanged(); 179: } 180: 181: /** 182: * Returns the number of series in the collection. 183: * 184: * @return The series count. 185: */ 186: public int getSeriesCount() { 187: return this.data.size(); 188: } 189: 190: /** 191: * Returns a list of all the series in the collection. 192: * 193: * @return The list (which is unmodifiable). 194: */ 195: public List getSeries() { 196: return Collections.unmodifiableList(this.data); 197: } 198: 199: /** 200: * Returns a series from the collection. 201: * 202: * @param series the series index (zero-based). 203: * 204: * @return The series. 205: * 206: * @throws IllegalArgumentException if <code>series</code> is not in the 207: * range <code>0</code> to <code>getSeriesCount() - 1</code>. 208: */ 209: public XYSeries getSeries(int series) { 210: if ((series < 0) || (series >= getSeriesCount())) { 211: throw new IllegalArgumentException("Series index out of bounds"); 212: } 213: return (XYSeries) this.data.get(series); 214: } 215: 216: /** 217: * Returns the key for a series. 218: * 219: * @param series the series index (in the range <code>0</code> to 220: * <code>getSeriesCount() - 1</code>). 221: * 222: * @return The key for a series. 223: * 224: * @throws IllegalArgumentException if <code>series</code> is not in the 225: * specified range. 226: */ 227: public Comparable getSeriesKey(int series) { 228: // defer argument checking 229: return getSeries(series).getKey(); 230: } 231: 232: /** 233: * Returns the number of items in the specified series. 234: * 235: * @param series the series (zero-based index). 236: * 237: * @return The item count. 238: * 239: * @throws IllegalArgumentException if <code>series</code> is not in the 240: * range <code>0</code> to <code>getSeriesCount() - 1</code>. 241: */ 242: public int getItemCount(int series) { 243: // defer argument checking 244: return getSeries(series).getItemCount(); 245: } 246: 247: /** 248: * Returns the x-value for the specified series and item. 249: * 250: * @param series the series (zero-based index). 251: * @param item the item (zero-based index). 252: * 253: * @return The value. 254: */ 255: public Number getX(int series, int item) { 256: XYSeries ts = (XYSeries) this.data.get(series); 257: XYDataItem xyItem = ts.getDataItem(item); 258: return xyItem.getX(); 259: } 260: 261: /** 262: * Returns the starting X value for the specified series and item. 263: * 264: * @param series the series (zero-based index). 265: * @param item the item (zero-based index). 266: * 267: * @return The starting X value. 268: */ 269: public Number getStartX(int series, int item) { 270: return this.intervalDelegate.getStartX(series, item); 271: } 272: 273: /** 274: * Returns the ending X value for the specified series and item. 275: * 276: * @param series the series (zero-based index). 277: * @param item the item (zero-based index). 278: * 279: * @return The ending X value. 280: */ 281: public Number getEndX(int series, int item) { 282: return this.intervalDelegate.getEndX(series, item); 283: } 284: 285: /** 286: * Returns the y-value for the specified series and item. 287: * 288: * @param series the series (zero-based index). 289: * @param index the index of the item of interest (zero-based). 290: * 291: * @return The value (possibly <code>null</code>). 292: */ 293: public Number getY(int series, int index) { 294: 295: XYSeries ts = (XYSeries) this.data.get(series); 296: XYDataItem xyItem = ts.getDataItem(index); 297: return xyItem.getY(); 298: 299: } 300: 301: /** 302: * Returns the starting Y value for the specified series and item. 303: * 304: * @param series the series (zero-based index). 305: * @param item the item (zero-based index). 306: * 307: * @return The starting Y value. 308: */ 309: public Number getStartY(int series, int item) { 310: return getY(series, item); 311: } 312: 313: /** 314: * Returns the ending Y value for the specified series and item. 315: * 316: * @param series the series (zero-based index). 317: * @param item the item (zero-based index). 318: * 319: * @return The ending Y value. 320: */ 321: public Number getEndY(int series, int item) { 322: return getY(series, item); 323: } 324: 325: /** 326: * Tests this collection for equality with an arbitrary object. 327: * 328: * @param obj the object (<code>null</code> permitted). 329: * 330: * @return A boolean. 331: */ 332: public boolean equals(Object obj) { 333: /* 334: * XXX 335: * 336: * what about the interval delegate...? 337: * The interval width etc wasn't considered 338: * before, hence i did not add it here (AS) 339: * 340: */ 341: 342: if (obj == this) { 343: return true; 344: } 345: if (!(obj instanceof XYSeriesCollection)) { 346: return false; 347: } 348: XYSeriesCollection that = (XYSeriesCollection) obj; 349: return ObjectUtilities.equal(this.data, that.data); 350: } 351: 352: /** 353: * Returns a clone of this instance. 354: * 355: * @return A clone. 356: * 357: * @throws CloneNotSupportedException if there is a problem. 358: */ 359: public Object clone() throws CloneNotSupportedException { 360: XYSeriesCollection clone = (XYSeriesCollection) super.clone(); 361: clone.data = (List) ObjectUtilities.deepClone(this.data); 362: clone.intervalDelegate 363: = (IntervalXYDelegate) this.intervalDelegate.clone(); 364: return clone; 365: } 366: 367: /** 368: * Returns a hash code. 369: * 370: * @return A hash code. 371: */ 372: public int hashCode() { 373: // Same question as for equals (AS) 374: return (this.data != null ? this.data.hashCode() : 0); 375: } 376: 377: /** 378: * Returns the minimum x-value in the dataset. 379: * 380: * @param includeInterval a flag that determines whether or not the 381: * x-interval is taken into account. 382: * 383: * @return The minimum value. 384: */ 385: public double getDomainLowerBound(boolean includeInterval) { 386: return this.intervalDelegate.getDomainLowerBound(includeInterval); 387: } 388: 389: /** 390: * Returns the maximum x-value in the dataset. 391: * 392: * @param includeInterval a flag that determines whether or not the 393: * x-interval is taken into account. 394: * 395: * @return The maximum value. 396: */ 397: public double getDomainUpperBound(boolean includeInterval) { 398: return this.intervalDelegate.getDomainUpperBound(includeInterval); 399: } 400: 401: /** 402: * Returns the range of the values in this dataset's domain. 403: * 404: * @param includeInterval a flag that determines whether or not the 405: * x-interval is taken into account. 406: * 407: * @return The range. 408: */ 409: public Range getDomainBounds(boolean includeInterval) { 410: if (includeInterval) { 411: return this.intervalDelegate.getDomainBounds(includeInterval); 412: } 413: else { 414: return DatasetUtilities.iterateDomainBounds(this, includeInterval); 415: } 416: 417: } 418: 419: /** 420: * Returns the interval width. This is used to calculate the start and end 421: * x-values, if/when the dataset is used as an {@link IntervalXYDataset}. 422: * 423: * @return The interval width. 424: */ 425: public double getIntervalWidth() { 426: return this.intervalDelegate.getIntervalWidth(); 427: } 428: 429: /** 430: * Sets the interval width and sends a {@link DatasetChangeEvent} to all 431: * registered listeners. 432: * 433: * @param width the width (negative values not permitted). 434: */ 435: public void setIntervalWidth(double width) { 436: if (width < 0.0) { 437: throw new IllegalArgumentException("Negative 'width' argument."); 438: } 439: this.intervalDelegate.setFixedIntervalWidth(width); 440: fireDatasetChanged(); 441: } 442: 443: /** 444: * Returns the interval position factor. 445: * 446: * @return The interval position factor. 447: */ 448: public double getIntervalPositionFactor() { 449: return this.intervalDelegate.getIntervalPositionFactor(); 450: } 451: 452: /** 453: * Sets the interval position factor. This controls where the x-value is in 454: * relation to the interval surrounding the x-value (0.0 means the x-value 455: * will be positioned at the start, 0.5 in the middle, and 1.0 at the end). 456: * 457: * @param factor the factor. 458: */ 459: public void setIntervalPositionFactor(double factor) { 460: this.intervalDelegate.setIntervalPositionFactor(factor); 461: fireDatasetChanged(); 462: } 463: 464: /** 465: * Returns whether the interval width is automatically calculated or not. 466: * 467: * @return Whether the width is automatically calculated or not. 468: */ 469: public boolean isAutoWidth() { 470: return this.intervalDelegate.isAutoWidth(); 471: } 472: 473: /** 474: * Sets the flag that indicates wether the interval width is automatically 475: * calculated or not. 476: * 477: * @param b a boolean. 478: */ 479: public void setAutoWidth(boolean b) { 480: this.intervalDelegate.setAutoWidth(b); 481: fireDatasetChanged(); 482: } 483: 484: }