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