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