001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * XYSeriesCollection.java 029 * ----------------------- 030 * (C) Copyright 2001-2005, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Aaron Metzger; 034 * 035 * $Id: XYSeriesCollection.java,v 1.12.2.2 2005/10/25 21:36:51 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 15-Nov-2001 : Version 1 (DG); 040 * 03-Apr-2002 : Added change listener code (DG); 041 * 29-Apr-2002 : Added removeSeries, removeAllSeries methods (ARM); 042 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 043 * 26-Mar-2003 : Implemented Serializable (DG); 044 * 04-Aug-2003 : Added getSeries() method (DG); 045 * 31-Mar-2004 : Modified to use an XYIntervalDelegate. 046 * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG); 047 * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG); 048 * 17-Nov-2004 : Updated for changes to DomainInfo interface (DG); 049 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG); 050 * 28-Mar-2005 : Fixed bug in getSeries(int) method (1170825) (DG); 051 * 05-Oct-2005 : Made the interval delegate a dataset listener (DG); 052 * 053 */ 054 055 package org.jfree.data.xy; 056 057 import java.io.Serializable; 058 import java.util.Collections; 059 import java.util.List; 060 061 import org.jfree.data.DomainInfo; 062 import org.jfree.data.Range; 063 import org.jfree.data.general.DatasetChangeEvent; 064 import org.jfree.data.general.DatasetUtilities; 065 import org.jfree.util.ObjectUtilities; 066 067 /** 068 * Represents a collection of {@link XYSeries} objects that can be used as a 069 * dataset. 070 */ 071 public class XYSeriesCollection extends AbstractIntervalXYDataset 072 implements IntervalXYDataset, DomainInfo, 073 Serializable { 074 075 /** For serialization. */ 076 private static final long serialVersionUID = -7590013825931496766L; 077 078 /** The series that are included in the collection. */ 079 private List data; 080 081 /** The interval delegate (used to calculate the start and end x-values). */ 082 private IntervalXYDelegate intervalDelegate; 083 084 /** 085 * Constructs an empty dataset. 086 */ 087 public XYSeriesCollection() { 088 this(null); 089 } 090 091 /** 092 * Constructs a dataset and populates it with a single series. 093 * 094 * @param series the series (<code>null</code> ignored). 095 */ 096 public XYSeriesCollection(XYSeries series) { 097 this.data = new java.util.ArrayList(); 098 this.intervalDelegate = new IntervalXYDelegate(this, false); 099 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 public XYSeries getSeries(int series) { 205 if ((series < 0) || (series >= getSeriesCount())) { 206 throw new IllegalArgumentException("Series index out of bounds"); 207 } 208 return (XYSeries) this.data.get(series); 209 } 210 211 /** 212 * Returns the key for a series. 213 * 214 * @param series the series index (zero-based). 215 * 216 * @return The key for a series. 217 */ 218 public Comparable getSeriesKey(int series) { 219 // defer argument checking 220 return getSeries(series).getKey(); 221 } 222 223 /** 224 * Returns the number of items in the specified series. 225 * 226 * @param series the series (zero-based index). 227 * 228 * @return The item count. 229 */ 230 public int getItemCount(int series) { 231 // defer argument checking 232 return getSeries(series).getItemCount(); 233 } 234 235 /** 236 * Returns the x-value for the specified series and item. 237 * 238 * @param series the series (zero-based index). 239 * @param item the item (zero-based index). 240 * 241 * @return The value. 242 */ 243 public Number getX(int series, int item) { 244 XYSeries ts = (XYSeries) this.data.get(series); 245 XYDataItem xyItem = ts.getDataItem(item); 246 return xyItem.getX(); 247 } 248 249 /** 250 * Returns the starting X value for the specified series and item. 251 * 252 * @param series the series (zero-based index). 253 * @param item the item (zero-based index). 254 * 255 * @return The starting X value. 256 */ 257 public Number getStartX(int series, int item) { 258 return this.intervalDelegate.getStartX(series, item); 259 } 260 261 /** 262 * Returns the ending 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 ending X value. 268 */ 269 public Number getEndX(int series, int item) { 270 return this.intervalDelegate.getEndX(series, item); 271 } 272 273 /** 274 * Returns the y-value for the specified series and item. 275 * 276 * @param series the series (zero-based index). 277 * @param index the index of the item of interest (zero-based). 278 * 279 * @return The value (possibly <code>null</code>). 280 */ 281 public Number getY(int series, int index) { 282 283 XYSeries ts = (XYSeries) this.data.get(series); 284 XYDataItem xyItem = ts.getDataItem(index); 285 return xyItem.getY(); 286 287 } 288 289 /** 290 * Returns the starting Y value for the specified series and item. 291 * 292 * @param series the series (zero-based index). 293 * @param item the item (zero-based index). 294 * 295 * @return The starting Y value. 296 */ 297 public Number getStartY(int series, int item) { 298 return getY(series, item); 299 } 300 301 /** 302 * Returns the ending 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 ending Y value. 308 */ 309 public Number getEndY(int series, int item) { 310 return getY(series, item); 311 } 312 313 /** 314 * Tests this collection for equality with an arbitrary object. 315 * 316 * @param obj the object (<code>null</code> permitted). 317 * 318 * @return A boolean. 319 */ 320 public boolean equals(Object obj) { 321 /* 322 * XXX 323 * 324 * what about the interval delegate...? 325 * The interval width etc wasn't considered 326 * before, hence i did not add it here (AS) 327 * 328 */ 329 330 if (obj == this) { 331 return true; 332 } 333 if (!(obj instanceof XYSeriesCollection)) { 334 return false; 335 } 336 XYSeriesCollection that = (XYSeriesCollection) obj; 337 return ObjectUtilities.equal(this.data, that.data); 338 } 339 340 /** 341 * Returns a hash code. 342 * 343 * @return A hash code. 344 */ 345 public int hashCode() { 346 // Same question as for equals (AS) 347 return (this.data != null ? this.data.hashCode() : 0); 348 } 349 350 /** 351 * Returns the minimum x-value in the dataset. 352 * 353 * @param includeInterval a flag that determines whether or not the 354 * x-interval is taken into account. 355 * 356 * @return The minimum value. 357 */ 358 public double getDomainLowerBound(boolean includeInterval) { 359 return this.intervalDelegate.getDomainLowerBound(includeInterval); 360 } 361 362 /** 363 * Returns the maximum x-value in the dataset. 364 * 365 * @param includeInterval a flag that determines whether or not the 366 * x-interval is taken into account. 367 * 368 * @return The maximum value. 369 */ 370 public double getDomainUpperBound(boolean includeInterval) { 371 return this.intervalDelegate.getDomainUpperBound(includeInterval); 372 } 373 374 /** 375 * Returns the range of the values in this dataset's domain. 376 * 377 * @param includeInterval a flag that determines whether or not the 378 * x-interval is taken into account. 379 * 380 * @return The range. 381 */ 382 public Range getDomainBounds(boolean includeInterval) { 383 if (includeInterval) { 384 return this.intervalDelegate.getDomainBounds(includeInterval); 385 } 386 else { 387 return DatasetUtilities.iterateDomainBounds(this, includeInterval); 388 } 389 390 } 391 392 /** 393 * Returns the interval width. This is used to calculate the start and end 394 * x-values, if/when the dataset is used as an {@link IntervalXYDataset}. 395 * 396 * @return The interval width. 397 */ 398 public double getIntervalWidth() { 399 return this.intervalDelegate.getIntervalWidth(); 400 } 401 402 /** 403 * Sets the interval width and sends a {@link DatasetChangeEvent} to all 404 * registered listeners. 405 * 406 * @param width the width (negative values not permitted). 407 */ 408 public void setIntervalWidth(double width) { 409 if (width < 0.0) { 410 throw new IllegalArgumentException("Negative 'width' argument."); 411 } 412 this.intervalDelegate.setFixedIntervalWidth(width); 413 fireDatasetChanged(); 414 } 415 416 /** 417 * Returns the interval position factor. 418 * 419 * @return The interval position factor. 420 */ 421 public double getIntervalPositionFactor() { 422 return this.intervalDelegate.getIntervalPositionFactor(); 423 } 424 425 /** 426 * Sets the interval position factor. This controls where the x-value is in 427 * relation to the interval surrounding the x-value (0.0 means the x-value 428 * will be positioned at the start, 0.5 in the middle, and 1.0 at the end). 429 * 430 * @param factor the factor. 431 */ 432 public void setIntervalPositionFactor(double factor) { 433 this.intervalDelegate.setIntervalPositionFactor(factor); 434 fireDatasetChanged(); 435 } 436 437 /** 438 * Returns whether the interval width is automatically calculated or not. 439 * 440 * @return Whether the width is automatically calculated or not. 441 */ 442 public boolean isAutoWidth() { 443 return this.intervalDelegate.isAutoWidth(); 444 } 445 446 /** 447 * Sets the flag that indicates wether the interval width is automatically 448 * calculated or not. 449 * 450 * @param b a boolean. 451 */ 452 public void setAutoWidth(boolean b) { 453 this.intervalDelegate.setAutoWidth(b); 454 fireDatasetChanged(); 455 } 456 457 }