001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, 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 * AbstractDataset.java 029 * -------------------- 030 * (C)opyright 2000-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Nicolas Brodu (for Astrium and EADS Corporate Research 034 * Center); 035 Tracy Hiltbrand (added equals/canEqual/hashCode); 036 * 037 */ 038 039package org.jfree.data.general; 040 041import java.io.IOException; 042import java.io.InvalidObjectException; 043import java.io.ObjectInputStream; 044import java.io.ObjectInputValidation; 045import java.io.ObjectOutputStream; 046import java.io.Serializable; 047import java.util.Arrays; 048import java.util.EventListener; 049import java.util.List; 050import java.util.Objects; 051 052import javax.swing.event.EventListenerList; 053import org.jfree.chart.util.Args; 054 055/** 056 * An abstract implementation of the {@link Dataset} interface, containing a 057 * mechanism for registering change listeners. 058 */ 059public abstract class AbstractDataset implements Dataset, Cloneable, 060 Serializable, ObjectInputValidation { 061 062 /** For serialization. */ 063 private static final long serialVersionUID = 1918768939869230744L; 064 065 /** The group that the dataset belongs to. */ 066 private DatasetGroup group; 067 068 /** Storage for registered change listeners. */ 069 private transient EventListenerList listenerList; 070 071 /** 072 * A flag that can be used to temporarily suppress dataset change event 073 * notifications. 074 */ 075 private boolean notify; 076 077 @Override 078 public int hashCode() { 079 int hash = 3; 080 hash = 29 * hash + Objects.hashCode(this.group); 081 hash = 29 * hash + (this.notify ? 1 : 0); 082 return hash; 083 } 084 085 /** 086 * Ensures symmetry between super/subclass implementations of equals. For 087 * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. 088 * 089 * @param other Object 090 * 091 * @return true ONLY if the parameter is THIS class type 092 */ 093 public boolean canEqual(Object other) { 094 // fix the "equals not symmetric" problem 095 return (other instanceof AbstractDataset); 096 } 097 098 @Override 099 public boolean equals(Object obj) { 100 if (this == obj) { 101 return true; 102 } 103 if (!(obj instanceof AbstractDataset)) { 104 return false; 105 } 106 107 AbstractDataset that = (AbstractDataset) obj; 108 if (this.notify != that.notify) { 109 return false; 110 } 111 if (!Objects.equals(this.group, that.group)) { 112 return false; 113 } 114 if (!that.canEqual(this)) { 115 return false; 116 } 117 return true; 118 } 119 120 /** 121 * Constructs a dataset. By default, the dataset is assigned to its own 122 * group. 123 */ 124 protected AbstractDataset() { 125 this.group = new DatasetGroup(); 126 this.listenerList = new EventListenerList(); 127 this.notify = true; 128 } 129 130 /** 131 * Returns the dataset group for the dataset. 132 * 133 * @return The group (never {@code null}). 134 * 135 * @see #setGroup(DatasetGroup) 136 */ 137 @Override 138 public DatasetGroup getGroup() { 139 return this.group; 140 } 141 142 /** 143 * Sets the dataset group for the dataset. 144 * 145 * @param group the group ({@code null} not permitted). 146 * 147 * @see #getGroup() 148 */ 149 @Override 150 public void setGroup(DatasetGroup group) { 151 Args.nullNotPermitted(group, "group"); 152 this.group = group; 153 } 154 155 /** 156 * Returns the value of the notify flag. The default value is 157 * {@code true}. If this is {@code false}, calls to the 158 * {@link #fireDatasetChanged()} method will NOT trigger a dataset 159 * change event. 160 * 161 * @return A boolean. 162 */ 163 public boolean getNotify() { 164 return this.notify; 165 } 166 167 /** 168 * Sets the notify flag, which controls whether or not the {@link #fireDatasetChanged()} 169 * method notifies listeners. Setting this flag to {@code true} will 170 * trigger a {@code DatasetChangeEvent} because there may be 171 * queued up changes. 172 * 173 * @param notify the new flag value. 174 */ 175 public void setNotify(boolean notify) { 176 this.notify = notify; 177 if (notify) { 178 fireDatasetChanged(); 179 } 180 } 181 182 /** 183 * Registers an object to receive notification of changes to the dataset. 184 * 185 * @param listener the object to register. 186 * 187 * @see #removeChangeListener(DatasetChangeListener) 188 */ 189 @Override 190 public void addChangeListener(DatasetChangeListener listener) { 191 this.listenerList.add(DatasetChangeListener.class, listener); 192 } 193 194 /** 195 * Deregisters an object so that it no longer receives notification of 196 * changes to the dataset. 197 * 198 * @param listener the object to deregister. 199 * 200 * @see #addChangeListener(DatasetChangeListener) 201 */ 202 @Override 203 public void removeChangeListener(DatasetChangeListener listener) { 204 this.listenerList.remove(DatasetChangeListener.class, listener); 205 } 206 207 /** 208 * Returns {@code true} if the specified object is registered with 209 * the dataset as a listener. Most applications won't need to call this 210 * method, it exists mainly for use by unit testing code. 211 * 212 * @param listener the listener. 213 * 214 * @return A boolean. 215 * 216 * @see #addChangeListener(DatasetChangeListener) 217 * @see #removeChangeListener(DatasetChangeListener) 218 */ 219 public boolean hasListener(EventListener listener) { 220 List list = Arrays.asList(this.listenerList.getListenerList()); 221 return list.contains(listener); 222 } 223 224 /** 225 * Notifies all registered listeners that the dataset has changed, 226 * provided that the {@code notify} flag has not been set to 227 * {@code false}. 228 * 229 * @see #addChangeListener(DatasetChangeListener) 230 */ 231 protected void fireDatasetChanged() { 232 if (this.notify) { 233 notifyListeners(new DatasetChangeEvent(this, this)); 234 } 235 } 236 237 /** 238 * Notifies all registered listeners that the dataset has changed. 239 * 240 * @param event contains information about the event that triggered the 241 * notification. 242 * 243 * @see #addChangeListener(DatasetChangeListener) 244 * @see #removeChangeListener(DatasetChangeListener) 245 */ 246 protected void notifyListeners(DatasetChangeEvent event) { 247 Object[] listeners = this.listenerList.getListenerList(); 248 for (int i = listeners.length - 2; i >= 0; i -= 2) { 249 if (listeners[i] == DatasetChangeListener.class) { 250 ((DatasetChangeListener) listeners[i + 1]).datasetChanged( 251 event); 252 } 253 } 254 } 255 256 /** 257 * Returns a clone of the dataset. The cloned dataset will NOT include the 258 * {@link DatasetChangeListener} references that have been registered with 259 * this dataset. 260 * 261 * @return A clone. 262 * 263 * @throws CloneNotSupportedException if the dataset does not support 264 * cloning. 265 */ 266 @Override 267 public Object clone() throws CloneNotSupportedException { 268 AbstractDataset clone = (AbstractDataset) super.clone(); 269 clone.listenerList = new EventListenerList(); 270 return clone; 271 } 272 273 /** 274 * Handles serialization. 275 * 276 * @param stream the output stream. 277 * 278 * @throws IOException if there is an I/O problem. 279 */ 280 private void writeObject(ObjectOutputStream stream) throws IOException { 281 stream.defaultWriteObject(); 282 } 283 284 /** 285 * Restores a serialized object. 286 * 287 * @param stream the input stream. 288 * 289 * @throws IOException if there is an I/O problem. 290 * @throws ClassNotFoundException if there is a problem loading a class. 291 */ 292 private void readObject(ObjectInputStream stream) 293 throws IOException, ClassNotFoundException { 294 stream.defaultReadObject(); 295 this.listenerList = new EventListenerList(); 296 stream.registerValidation(this, 10); // see comments about priority of 297 // 10 in validateObject() 298 } 299 300 /** 301 * Validates the object. We use this opportunity to call listeners who have 302 * registered during the deserialization process, as listeners are not 303 * serialized. This method is called by the serialization system after the 304 * entire graph is read. 305 * 306 * This object has registered itself to the system with a priority of 10. 307 * Other callbacks may register with a higher priority number to be called 308 * before this object, or with a lower priority number to be called after 309 * the listeners were notified. 310 * 311 * All listeners are supposed to have register by now, either in their 312 * readObject or validateObject methods. Notify them that this dataset has 313 * changed. 314 * 315 * @exception InvalidObjectException If the object cannot validate itself. 316 */ 317 @Override 318 public void validateObject() throws InvalidObjectException { 319 fireDatasetChanged(); 320 } 321 322}