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; 038import javax.swing.event.EventListenerList; 039import org.jfree.chart3d.data.Series3DChangeEvent; 040import org.jfree.chart3d.data.Series3DChangeListener; 041import org.jfree.chart3d.internal.Args; 042import org.jfree.chart3d.internal.ObjectUtils; 043 044/** 045 * A data series containing a sequence of {@code (x, y, z)} data items. 046 * The series has an immutable key to identify it, and can be added to an 047 * {@link XYZSeriesCollection} to create a dataset. When a series is part 048 * of an {@link XYZSeriesCollection}, the collection will register with the 049 * series to receive change events - in this way, the collection can notify 050 * its own listeners when a change is made to the series. 051 * <br><br> 052 * NOTE: This class is serializable, but the serialization format is subject 053 * to change in future releases and should not be relied upon for persisting 054 * instances of this class. 055 * 056 * @param <K> the type for the series key (it is recommended that this is a 057 * class of immutable objects, because the series key should never be 058 * modified). 059 */ 060@SuppressWarnings("serial") 061public class XYZSeries<K extends Comparable<K>> implements Serializable { 062 063 /** The series key (never {@code null}). */ 064 private final K key; 065 066 /** The data items in the series. */ 067 private final List<XYZDataItem> items; 068 069 /** Storage for registered change listeners. */ 070 private EventListenerList listeners; 071 072 /** A flag that controls whether or not changes are notified. */ 073 private boolean notify; 074 075 /** 076 * Creates a new series with the specified key. Note that the series key 077 * cannot be changed after it has been set in the constructor - this is by 078 * design, to ensure that each series in a {@link XYZSeriesCollection} 079 * always has a unique key. For the same reason, the key type should be 080 * an immutable class. 081 * 082 * @param key the key ({@code null} not permitted). 083 */ 084 public XYZSeries(K key) { 085 Args.nullNotPermitted(key, "key"); 086 this.key = key; 087 this.items = new ArrayList<>(); 088 this.listeners = new EventListenerList(); 089 this.notify = true; 090 } 091 092 /** 093 * Returns the series key. 094 * 095 * @return The series key (never {@code null}). 096 */ 097 public K getKey() { 098 return this.key; 099 } 100 101 /** 102 * Returns the number of items in the series. 103 * 104 * @return The number of items in the series. 105 */ 106 public int getItemCount() { 107 return this.items.size(); 108 } 109 110 /** 111 * Returns a list containing all the items for the dataset (a new list 112 * is created each time this method is called, so the list can be freely 113 * modified without affecting the state of this series). 114 * 115 * @return A list of all items. 116 * 117 * @since 1.6 118 */ 119 public List<XYZDataItem> getItems() { 120 return new ArrayList<>(this.items); 121 } 122 123 /** 124 * Returns the x-value for the specified item in the series. 125 * 126 * @param itemIndex the item index. 127 * 128 * @return The x-value. 129 */ 130 public double getXValue(int itemIndex) { 131 return this.items.get(itemIndex).getX(); 132 } 133 134 /** 135 * Returns the y-value for the specified item in the series. 136 * 137 * @param itemIndex the item index. 138 * 139 * @return The y-value. 140 */ 141 public double getYValue(int itemIndex) { 142 return this.items.get(itemIndex).getY(); 143 } 144 145 /** 146 * Returns the z-value for the specified item in the series. 147 * 148 * @param itemIndex the item index. 149 * 150 * @return The z-value. 151 */ 152 public double getZValue(int itemIndex) { 153 return this.items.get(itemIndex).getZ(); 154 } 155 156 /** 157 * Adds a new data item to the series and sends a 158 * {@link Series3DChangeEvent} to all registered listeners. 159 * 160 * @param x the x-value. 161 * @param y the y-value. 162 * @param z the z-value. 163 */ 164 public void add(double x, double y, double z) { 165 add(new XYZDataItem(x, y, z)); 166 } 167 168 /** 169 * Adds a new data item to the series and sends a 170 * {@link Series3DChangeEvent} to all registered listeners. 171 * 172 * @param item the data item ({@code null} not permitted). 173 */ 174 public void add(XYZDataItem item) { 175 Args.nullNotPermitted(item, "item"); 176 this.items.add(item); 177 fireSeriesChanged(); 178 } 179 180 /** 181 * Removes a data item from the series and sends a 182 * {@link Series3DChangeEvent} to all registered listeners. 183 * 184 * @param itemIndex the item index. 185 * 186 * @since 1.6 187 */ 188 public void remove(int itemIndex) { 189 this.items.remove(itemIndex); 190 fireSeriesChanged(); 191 } 192 193 /** 194 * Registers an object with this series, to receive notification whenever 195 * the series changes. 196 * <P> 197 * Objects being registered must implement the 198 * {@link Series3DChangeListener} interface. 199 * 200 * @param listener the listener to register. 201 * 202 * @since 1.6 203 */ 204 public void addChangeListener(Series3DChangeListener listener) { 205 this.listeners.add(Series3DChangeListener.class, listener); 206 } 207 208 /** 209 * Deregisters an object, so that it not longer receives notification 210 * whenever the series changes. 211 * 212 * @param listener the listener to deregister. 213 * 214 * @since 1.6 215 */ 216 public void removeChangeListener(Series3DChangeListener listener) { 217 this.listeners.remove(Series3DChangeListener.class, listener); 218 } 219 220 /** 221 * Returns the flag that controls whether or not change events are sent to 222 * registered listeners. 223 * 224 * @return A boolean. 225 * 226 * @see #setNotify(boolean) 227 * @since 1.6 228 */ 229 public boolean getNotify() { 230 return this.notify; 231 } 232 233 /** 234 * Sets the flag that controls whether or not change events are sent to 235 * registered listeners. 236 * 237 * @param notify the new value of the flag. 238 * 239 * @see #getNotify() 240 * @since 1.6 241 */ 242 public void setNotify(boolean notify) { 243 if (this.notify != notify) { 244 this.notify = notify; 245 if (notify) { 246 fireSeriesChanged(); 247 } 248 } 249 } 250 251 /** 252 * General method for signaling to registered listeners that the series 253 * has been changed. 254 * 255 * @since 1.6 256 */ 257 public void fireSeriesChanged() { 258 if (this.notify) { 259 notifyListeners(new Series3DChangeEvent(this)); 260 } 261 } 262 263 /** 264 * Sends a change event to all registered listeners. 265 * 266 * @param event contains information about the event that triggered the 267 * notification. 268 * 269 * @since 1.6 270 */ 271 protected void notifyListeners(Series3DChangeEvent event) { 272 Object[] listenerList = this.listeners.getListenerList(); 273 for (int i = listenerList.length - 2; i >= 0; i -= 2) { 274 if (listenerList[i] == Series3DChangeListener.class) { 275 ((Series3DChangeListener) listenerList[i + 1]).seriesChanged( 276 event); 277 } 278 } 279 } 280 281 /** 282 * Tests this series for equality with an arbitrary object. 283 * 284 * @param obj the object to test ({@code null} permitted). 285 * 286 * @return A boolean. 287 */ 288 @Override 289 public boolean equals(Object obj) { 290 if (obj == this) { 291 return true; 292 } 293 if (!(obj instanceof XYZSeries)) { 294 return false; 295 } 296 XYZSeries that = (XYZSeries) obj; 297 if (!this.key.equals(that.key)) { 298 return false; 299 } 300 if (!this.items.equals(that.items)) { 301 return false; 302 } 303 return true; 304 } 305 306 @Override 307 public int hashCode() { 308 int hash = 7; 309 hash = 41 * hash + ObjectUtils.hashCode(this.key); 310 return hash; 311 } 312 313}