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