001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2022, by David Gilbert. All rights reserved. 006 * 007 * https://github.com/jfree/orson-charts 008 * 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as published by 011 * the Free Software Foundation, either version 3 of the License, or 012 * (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * along with this program. If not, see <http://www.gnu.org/licenses/>. 021 * 022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 023 * Other names may be trademarks of their respective owners.] 024 * 025 * If you do not wish to be bound by the terms of the GPL, an alternative 026 * commercial license can be purchased. For details, please see visit the 027 * Orson Charts home page: 028 * 029 * http://www.object-refinery.com/orsoncharts/index.html 030 * 031 */ 032 033package org.jfree.chart3d.data.xyz; 034 035import java.io.Serializable; 036import java.util.ArrayList; 037import java.util.List; 038 039import org.jfree.chart3d.internal.Args; 040import org.jfree.chart3d.data.AbstractDataset3D; 041import org.jfree.chart3d.data.Dataset3DChangeEvent; 042import org.jfree.chart3d.data.JSONUtils; 043import org.jfree.chart3d.data.Series3DChangeEvent; 044import org.jfree.chart3d.data.Series3DChangeListener; 045import org.jfree.chart3d.internal.ObjectUtils; 046import org.jfree.chart3d.plot.XYZPlot; 047import org.jfree.chart3d.renderer.xyz.XYZRenderer; 048 049/** 050 * A collection of {@link XYZSeries} objects (implements the {@link XYZDataset} 051 * interface so that it can be used as a source of data for an 052 * {@link XYZRenderer} on an {@link XYZPlot}). 053 * <br><br> 054 * NOTE: This class is serializable, but the serialization format is subject 055 * to change in future releases and should not be relied upon for persisting 056 * instances of this class. 057 */ 058@SuppressWarnings("serial") 059public class XYZSeriesCollection<S extends Comparable<S>> 060 extends AbstractDataset3D 061 implements XYZDataset<S>, Series3DChangeListener, Serializable { 062 063 /** Storage for the data series. */ 064 private final List<XYZSeries<S>> series; 065 066 /** 067 * Creates a new (empty) {@code XYZSeriesCollection} instance. 068 */ 069 public XYZSeriesCollection() { 070 this.series = new ArrayList<>(); 071 } 072 073 /** 074 * Returns the number of series in the collection. 075 * 076 * @return The number of series in the collection. 077 */ 078 @Override 079 public int getSeriesCount() { 080 return this.series.size(); 081 } 082 083 /** 084 * Returns the index of the series with the specified key, or 085 * {@code -1} if there is no series with the specified key. 086 * 087 * @param key the key ({@code null} not permitted). 088 * 089 * @return The series index or {@code -1}. 090 */ 091 @Override 092 public int getSeriesIndex(S key) { 093 Args.nullNotPermitted(key, "key"); 094 return getSeriesKeys().indexOf(key); 095 } 096 097 /** 098 * Returns a new list containing all the series keys. Modifying this list 099 * will have no impact on the {@code XYZSeriesCollection} instance. 100 * 101 * @return A list containing the series keys (possibly empty, but never 102 * {@code null}). 103 */ 104 @Override 105 public List<S> getSeriesKeys() { 106 List<S> result = new ArrayList<>(); 107 for (XYZSeries<S> s : this.series) { 108 result.add(s.getKey()); 109 } 110 return result; 111 } 112 113 /** 114 * Returns the key for the specified series. 115 * 116 * @param seriesIndex the series index. 117 * 118 * @return The series key. 119 * 120 * @since 1.3 121 */ 122 @Override 123 public S getSeriesKey(int seriesIndex) { 124 return getSeries(seriesIndex).getKey(); 125 } 126 127 /** 128 * Adds a series to the collection (note that the series key must be 129 * unique within the collection). The collection will automatically 130 * register to receive change events from the series, and fire a 131 * {@link Dataset3DChangeEvent} whenever the data in the series changes. 132 * 133 * @param series the series ({@code null} not permitted). 134 */ 135 public void add(XYZSeries<S> series) { 136 Args.nullNotPermitted(series, "series"); 137 if (getSeriesIndex(series.getKey()) >= 0) { 138 throw new IllegalArgumentException("Another series with the same key already exists within the collection."); 139 } 140 this.series.add(series); 141 series.addChangeListener(this); 142 fireDatasetChanged(); 143 } 144 145 /** 146 * Removes a series from the collection and sends a 147 * {@link Dataset3DChangeEvent} to all registered listeners. 148 * 149 * @param seriesIndex the series index. 150 * 151 * @since 1.6 152 */ 153 public void remove(int seriesIndex) { 154 XYZSeries s = getSeries(seriesIndex); 155 remove(s); 156 } 157 158 /** 159 * Removes a series from the collection and sends a 160 * {@link Dataset3DChangeEvent} to all registered listeners. If the series 161 * is not part of the collection, this method does nothing. 162 * 163 * @param series the series ({@code null} not permitted). 164 * 165 * @since 1.6 166 */ 167 public void remove(XYZSeries series) { 168 Args.nullNotPermitted(series, "series"); 169 if (this.series.contains(series)) { 170 series.removeChangeListener(this); 171 this.series.remove(series); 172 fireDatasetChanged(); 173 } 174 } 175 176 /** 177 * Removes all the series from the collection and sends a 178 * {@link Dataset3DChangeEvent} to all registered listeners. If the 179 * collection is already empty, this method does nothing. 180 */ 181 public void removeAll() { 182 if (!this.series.isEmpty()) { 183 for (XYZSeries s : this.series) { 184 s.removeChangeListener(this); 185 } 186 this.series.clear(); 187 fireDatasetChanged(); 188 } 189 } 190 191 /** 192 * Returns the series with the specified index. 193 * 194 * @param index the series index. 195 * 196 * @return The series. 197 * 198 * @since 1.2 199 */ 200 public XYZSeries<S> getSeries(int index) { 201 Args.checkArrayBounds(index, "index", this.series.size()); 202 return this.series.get(index); 203 } 204 205 /** 206 * Returns the series with the specified key, or {@code null} if 207 * there is no such series. 208 * 209 * @param key the key ({@code null} not permitted). 210 * 211 * @return The series. 212 * 213 * @since 1.2 214 */ 215 public XYZSeries getSeries(Comparable<?> key) { 216 Args.nullNotPermitted(key, "key"); 217 for (XYZSeries s : this.series) { 218 if (s.getKey().equals(key)) { 219 return s; 220 } 221 } 222 return null; 223 } 224 225 /** 226 * Returns the number of items in the specified series. 227 * 228 * @param seriesIndex the series index. 229 * 230 * @return The number of items in the specified series. 231 */ 232 @Override 233 public int getItemCount(int seriesIndex) { 234 XYZSeries s = this.series.get(seriesIndex); 235 return s.getItemCount(); 236 } 237 238 /** 239 * Returns the x-value for one item in a series. 240 * 241 * @param seriesIndex the series index. 242 * @param itemIndex the item index. 243 * 244 * @return The x-value. 245 */ 246 @Override 247 public double getX(int seriesIndex, int itemIndex) { 248 XYZSeries s = this.series.get(seriesIndex); 249 return s.getXValue(itemIndex); 250 } 251 252 /** 253 * Returns the y-value for one item in a series. 254 * 255 * @param seriesIndex the series index. 256 * @param itemIndex the item index. 257 * 258 * @return The y-value. 259 */ 260 @Override 261 public double getY(int seriesIndex, int itemIndex) { 262 XYZSeries s = this.series.get(seriesIndex); 263 return s.getYValue(itemIndex); 264 } 265 266 /** 267 * Returns the z-value for one item in a series. 268 * 269 * @param seriesIndex the series index. 270 * @param itemIndex the item index. 271 * 272 * @return The z-value. 273 */ 274 @Override 275 public double getZ(int seriesIndex, int itemIndex) { 276 XYZSeries s = this.series.get(seriesIndex); 277 return s.getZValue(itemIndex); 278 } 279 280 /** 281 * Called when an observed series changes in some way. 282 * 283 * @param event information about the change. 284 * 285 * @since 1.6 286 */ 287 @Override 288 public void seriesChanged(Series3DChangeEvent event) { 289 fireDatasetChanged(); 290 } 291 292 /** 293 * Tests this dataset for equality with an arbitrary object. 294 * 295 * @param obj the object ({@code null} not permitted). 296 * 297 * @return A boolean. 298 */ 299 @Override 300 public boolean equals(Object obj) { 301 if (obj == this) { 302 return true; 303 } 304 if (!(obj instanceof XYZSeriesCollection)) { 305 return false; 306 } 307 XYZSeriesCollection that = (XYZSeriesCollection) obj; 308 if (!this.series.equals(that.series)) { 309 return false; 310 } 311 return true; 312 } 313 314 @Override 315 public int hashCode() { 316 int hash = 5; 317 hash = 59 * hash + ObjectUtils.hashCode(this.series); 318 return hash; 319 } 320 321 /** 322 * Returns a string representation of this instance, primarily for 323 * debugging purposes. 324 * <br><br> 325 * Implementation note: the current implementation (which is subject to 326 * change) writes the dataset in JSON format using 327 * {@link JSONUtils#writeXYZDataset(org.jfree.chart3d.data.xyz.XYZDataset)}. 328 * 329 * @return A string. 330 */ 331 @Override 332 public String toString() { 333 return JSONUtils.writeXYZDataset(this); 334 } 335 336}