Source for org.jfree.data.xy.XYSeriesCollection

   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: }