001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------------------ 028 * XIntervalSeriesCollection.java 029 * ------------------------------ 030 * (C) Copyright 2006-2022, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.data.xy; 038 039import java.io.IOException; 040import java.io.ObjectInputStream; 041import java.io.ObjectOutputStream; 042import java.io.Serializable; 043import java.util.ArrayList; 044import java.util.List; 045import java.util.Objects; 046 047import org.jfree.chart.internal.Args; 048import org.jfree.chart.internal.CloneUtils; 049import org.jfree.chart.api.PublicCloneable; 050 051import org.jfree.data.general.DatasetChangeEvent; 052 053/** 054 * A collection of {@link XIntervalSeries} objects. 055 * 056 * @since 1.0.3 057 * 058 * @see XIntervalSeries 059 */ 060public class XIntervalSeriesCollection<S extends Comparable<S>> 061 extends AbstractIntervalXYDataset 062 implements IntervalXYDataset, PublicCloneable, Serializable { 063 064 /** Storage for the data series. */ 065 private List<XIntervalSeries<S>> data; 066 067 /** 068 * Creates a new {@code XIntervalSeriesCollection} instance. 069 */ 070 public XIntervalSeriesCollection() { 071 this.data = new ArrayList<>(); 072 } 073 074 /** 075 * Adds a series to the collection and sends a {@link DatasetChangeEvent} 076 * to all registered listeners. 077 * 078 * @param series the series ({@code null} not permitted). 079 */ 080 public void addSeries(XIntervalSeries<S> series) { 081 Args.nullNotPermitted(series, "series"); 082 this.data.add(series); 083 series.addChangeListener(this); 084 fireDatasetChanged(); 085 } 086 087 /** 088 * Returns the number of series in the collection. 089 * 090 * @return The series count. 091 */ 092 @Override 093 public int getSeriesCount() { 094 return this.data.size(); 095 } 096 097 /** 098 * Returns a series from the collection. 099 * 100 * @param series the series index (zero-based). 101 * 102 * @return The series. 103 * 104 * @throws IllegalArgumentException if {@code series} is not in the 105 * range {@code 0} to {@code getSeriesCount() - 1}. 106 */ 107 public XIntervalSeries<S> getSeries(int series) { 108 Args.requireInRange(series, "series", 0, this.data.size() - 1); 109 return this.data.get(series); 110 } 111 112 /** 113 * Returns the key for a series. 114 * 115 * @param series the series index (in the range {@code 0} to 116 * {@code getSeriesCount() - 1}). 117 * 118 * @return The key for a series. 119 * 120 * @throws IllegalArgumentException if {@code series} is not in the 121 * specified range. 122 */ 123 @Override 124 public S getSeriesKey(int series) { 125 // defer argument checking 126 return getSeries(series).getKey(); 127 } 128 129 /** 130 * Returns the number of items in the specified series. 131 * 132 * @param series the series (zero-based index). 133 * 134 * @return The item count. 135 * 136 * @throws IllegalArgumentException if {@code series} is not in the 137 * range {@code 0} to {@code getSeriesCount() - 1}. 138 */ 139 @Override 140 public int getItemCount(int series) { 141 // defer argument checking 142 return getSeries(series).getItemCount(); 143 } 144 145 /** 146 * Returns the x-value for an item within a series. 147 * 148 * @param series the series index. 149 * @param item the item index. 150 * 151 * @return The x-value. 152 */ 153 @Override 154 public Number getX(int series, int item) { 155 XIntervalSeries<S> s = this.data.get(series); 156 XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); 157 return di.getX(); 158 } 159 160 /** 161 * Returns the start x-value (as a double primitive) for an item within a 162 * series. 163 * 164 * @param series the series index (zero-based). 165 * @param item the item index (zero-based). 166 * 167 * @return The value. 168 */ 169 @Override 170 public double getStartXValue(int series, int item) { 171 XIntervalSeries<S> s = this.data.get(series); 172 return s.getXLowValue(item); 173 } 174 175 /** 176 * Returns the end x-value (as a double primitive) for an item within a 177 * series. 178 * 179 * @param series the series (zero-based index). 180 * @param item the item (zero-based index). 181 * 182 * @return The value. 183 */ 184 @Override 185 public double getEndXValue(int series, int item) { 186 XIntervalSeries<S> s = this.data.get(series); 187 return s.getXHighValue(item); 188 } 189 190 /** 191 * Returns the y-value (as a double primitive) for an item within a 192 * series. 193 * 194 * @param series the series index (zero-based). 195 * @param item the item index (zero-based). 196 * 197 * @return The value. 198 */ 199 @Override 200 public double getYValue(int series, int item) { 201 XIntervalSeries<S> s = this.data.get(series); 202 return s.getYValue(item); 203 } 204 205 /** 206 * Returns the y-value for an item within a series. 207 * 208 * @param series the series index. 209 * @param item the item index. 210 * 211 * @return The y-value. 212 */ 213 @Override 214 public Number getY(int series, int item) { 215 XIntervalSeries<S> s = this.data.get(series); 216 XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); 217 return di.getYValue(); 218 } 219 220 /** 221 * Returns the start x-value for an item within a series. 222 * 223 * @param series the series index. 224 * @param item the item index. 225 * 226 * @return The x-value. 227 */ 228 @Override 229 public Number getStartX(int series, int item) { 230 XIntervalSeries<S> s = this.data.get(series); 231 XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); 232 return di.getXLowValue(); 233 } 234 235 /** 236 * Returns the end x-value for an item within a series. 237 * 238 * @param series the series index. 239 * @param item the item index. 240 * 241 * @return The x-value. 242 */ 243 @Override 244 public Number getEndX(int series, int item) { 245 XIntervalSeries<S> s = this.data.get(series); 246 XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); 247 return di.getXHighValue(); 248 } 249 250 /** 251 * Returns the start y-value for an item within a series. This method 252 * maps directly to {@link #getY(int, int)}. 253 * 254 * @param series the series index. 255 * @param item the item index. 256 * 257 * @return The start y-value. 258 */ 259 @Override 260 public Number getStartY(int series, int item) { 261 return getY(series, item); 262 } 263 264 /** 265 * Returns the end y-value for an item within a series. This method 266 * maps directly to {@link #getY(int, int)}. 267 * 268 * @param series the series index. 269 * @param item the item index. 270 * 271 * @return The end y-value. 272 */ 273 @Override 274 public Number getEndY(int series, int item) { 275 return getY(series, item); 276 } 277 278 /** 279 * Removes a series from the collection and sends a 280 * {@link DatasetChangeEvent} to all registered listeners. 281 * 282 * @param series the series index (zero-based). 283 * 284 * @since 1.0.10 285 */ 286 public void removeSeries(int series) { 287 Args.requireInRange(series, "series", 0, this.data.size() - 1); 288 XIntervalSeries ts = this.data.get(series); 289 ts.removeChangeListener(this); 290 this.data.remove(series); 291 fireDatasetChanged(); 292 } 293 294 /** 295 * Removes a series from the collection and sends a 296 * {@link DatasetChangeEvent} to all registered listeners. 297 * 298 * @param series the series ({@code null} not permitted). 299 * 300 * @since 1.0.10 301 */ 302 public void removeSeries(XIntervalSeries<S> series) { 303 Args.nullNotPermitted(series, "series"); 304 if (this.data.contains(series)) { 305 series.removeChangeListener(this); 306 this.data.remove(series); 307 fireDatasetChanged(); 308 } 309 } 310 311 /** 312 * Removes all the series from the collection and sends a 313 * {@link DatasetChangeEvent} to all registered listeners. 314 * 315 * @since 1.0.10 316 */ 317 public void removeAllSeries() { 318 // Unregister the collection as a change listener to each series in 319 // the collection. 320 for (XIntervalSeries series : this.data) { 321 series.removeChangeListener(this); 322 } 323 this.data.clear(); 324 fireDatasetChanged(); 325 } 326 327 /** 328 * Tests this instance for equality with an arbitrary object. 329 * 330 * @param obj the object ({@code null} permitted). 331 * 332 * @return A boolean. 333 */ 334 @Override 335 public boolean equals(Object obj) { 336 if (obj == this) { 337 return true; 338 } 339 if (!(obj instanceof XIntervalSeriesCollection)) { 340 return false; 341 } 342 XIntervalSeriesCollection<S> that = (XIntervalSeriesCollection<S>) obj; 343 return Objects.equals(this.data, that.data); 344 } 345 346 /** 347 * Returns a clone of this instance. 348 * 349 * @return A clone. 350 * 351 * @throws CloneNotSupportedException if there is a problem. 352 */ 353 @Override 354 public Object clone() throws CloneNotSupportedException { 355 XIntervalSeriesCollection clone 356 = (XIntervalSeriesCollection) super.clone(); 357 clone.data = CloneUtils.cloneList(this.data); 358 return clone; 359 } 360 361 362 /** 363 * Provides serialization support. 364 * 365 * @param stream the output stream. 366 * 367 * @throws IOException if there is an I/O error. 368 */ 369 private void writeObject(ObjectOutputStream stream) throws IOException { 370 stream.defaultWriteObject(); 371 } 372 373 /** 374 * Provides serialization support. 375 * 376 * @param stream the input stream. 377 * 378 * @throws IOException if there is an I/O error. 379 * @throws ClassNotFoundException if there is a classpath problem. 380 */ 381 private void readObject(ObjectInputStream stream) 382 throws IOException, ClassNotFoundException { 383 stream.defaultReadObject(); 384 for (XIntervalSeries<S> item : this.data) { 385 XIntervalSeries<S> series = item; 386 series.addChangeListener(this); 387 } 388 } 389}