001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * -------------------- 028 * AbstractDataset.java 029 * -------------------- 030 * (C)opyright 2000-2005, 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 * $Id: AbstractDataset.java,v 1.5.2.1 2005/10/25 21:32:29 mungady Exp $ 037 * 038 * Changes (from 21-Aug-2001) 039 * -------------------------- 040 * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG); 041 * 18-Sep-2001 : Updated e-mail address in header (DG); 042 * 15-Oct-2001 : Moved to new package (com.jrefinery.data.*) (DG); 043 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); 044 * 17-Nov-2001 : Changed constructor from public to protected, created new 045 * AbstractSeriesDataset class and transferred series-related 046 * methods, updated Javadoc comments (DG); 047 * 04-Mar-2002 : Updated import statements (DG); 048 * 11-Jun-2002 : Updated for change in the event constructor (DG); 049 * 07-Aug-2002 : Changed listener list to use 050 * javax.swing.event.EventListenerList (DG); 051 * 04-Oct-2002 : Fixed errors reported by Checkstyle (DG); 052 * 27-Mar-2003 : Implemented Serializable (DG); 053 * 18-Aug-2003 : Implemented Cloneable (DG); 054 * 08-Sep-2003 : Serialization fixes (NB); 055 * 11-Sep-2003 : Cloning Fixes (NB); 056 * 01-Jun-2005 : Added hasListener() method for unit testing (DG); 057 * 058 */ 059 060 package org.jfree.data.general; 061 062 import java.io.IOException; 063 import java.io.InvalidObjectException; 064 import java.io.ObjectInputStream; 065 import java.io.ObjectInputValidation; 066 import java.io.ObjectOutputStream; 067 import java.io.Serializable; 068 import java.util.Arrays; 069 import java.util.EventListener; 070 import java.util.List; 071 072 import javax.swing.event.EventListenerList; 073 074 /** 075 * An abstract implementation of the {@link Dataset} interface, containing a 076 * mechanism for registering change listeners. 077 */ 078 public abstract class AbstractDataset implements Dataset, 079 Cloneable, 080 Serializable, 081 ObjectInputValidation { 082 083 /** For serialization. */ 084 private static final long serialVersionUID = 1918768939869230744L; 085 086 /** The group that the dataset belongs to. */ 087 private DatasetGroup group; 088 089 /** Storage for registered change listeners. */ 090 private transient EventListenerList listenerList; 091 092 /** 093 * Constructs a dataset. By default, the dataset is assigned to its own 094 * group. 095 */ 096 protected AbstractDataset() { 097 this.group = new DatasetGroup(); 098 this.listenerList = new EventListenerList(); 099 } 100 101 /** 102 * Returns the dataset group for the dataset. 103 * 104 * @return The group. 105 */ 106 public DatasetGroup getGroup() { 107 return this.group; 108 } 109 110 /** 111 * Sets the dataset group for the dataset. 112 * 113 * @param group the group (<code>null</code> not permitted). 114 */ 115 public void setGroup(DatasetGroup group) { 116 if (group == null) { 117 throw new IllegalArgumentException("Null 'group' argument."); 118 } 119 this.group = group; 120 } 121 122 /** 123 * Registers an object to receive notification of changes to the dataset. 124 * 125 * @param listener the object to register. 126 */ 127 public void addChangeListener(DatasetChangeListener listener) { 128 this.listenerList.add(DatasetChangeListener.class, listener); 129 } 130 131 /** 132 * Deregisters an object so that it no longer receives notification of 133 * changes to the dataset. 134 * 135 * @param listener the object to deregister. 136 */ 137 public void removeChangeListener(DatasetChangeListener listener) { 138 this.listenerList.remove(DatasetChangeListener.class, listener); 139 } 140 141 /** 142 * Returns <code>true</code> if the specified object is registered with 143 * the dataset as a listener. Most applications won't need to call this 144 * method, it exists mainly for use by unit testing code. 145 * 146 * @param listener the listener. 147 * 148 * @return A boolean. 149 */ 150 public boolean hasListener(EventListener listener) { 151 List list = Arrays.asList(this.listenerList.getListenerList()); 152 return list.contains(listener); 153 } 154 155 /** 156 * Notifies all registered listeners that the dataset has changed. 157 */ 158 protected void fireDatasetChanged() { 159 notifyListeners( 160 new DatasetChangeEvent( 161 this, // source 162 this // dataset 163 ) 164 ); 165 } 166 167 /** 168 * Notifies all registered listeners that the dataset has changed. 169 * 170 * @param event contains information about the event that triggered the 171 * notification. 172 */ 173 protected void notifyListeners(DatasetChangeEvent event) { 174 175 Object[] listeners = this.listenerList.getListenerList(); 176 for (int i = listeners.length - 2; i >= 0; i -= 2) { 177 if (listeners[i] == DatasetChangeListener.class) { 178 ((DatasetChangeListener) listeners[i + 1]).datasetChanged( 179 event 180 ); 181 } 182 } 183 184 } 185 186 /** 187 * Returns a clone of the dataset. The cloned dataset will NOT include the 188 * {@link DatasetChangeListener} references that have been registered with 189 * this dataset. 190 * 191 * @return A clone. 192 * 193 * @throws CloneNotSupportedException if the dataset does not support 194 * cloning. 195 */ 196 public Object clone() throws CloneNotSupportedException { 197 AbstractDataset clone = (AbstractDataset) super.clone(); 198 clone.listenerList = new EventListenerList(); 199 return clone; 200 } 201 202 /** 203 * Handles serialization. 204 * 205 * @param stream the output stream. 206 * 207 * @throws IOException if there is an I/O problem. 208 */ 209 private void writeObject(ObjectOutputStream stream) throws IOException { 210 stream.defaultWriteObject(); 211 } 212 213 /** 214 * Restores a serialized object. 215 * 216 * @param stream the input stream. 217 * 218 * @throws IOException if there is an I/O problem. 219 * @throws ClassNotFoundException if there is a problem loading a class. 220 */ 221 private void readObject(ObjectInputStream stream) 222 throws IOException, ClassNotFoundException { 223 stream.defaultReadObject(); 224 this.listenerList = new EventListenerList(); 225 stream.registerValidation(this, 10); // see comments about priority of 226 // 10 in validateObject() 227 } 228 229 /** 230 * Validates the object. We use this opportunity to call listeners who have 231 * registered during the deserialization process, as listeners are not 232 * serialized. This method is called by the serialization system after the 233 * entire graph is read. 234 * 235 * This object has registered itself to the system with a priority of 10. 236 * Other callbacks may register with a higher priority number to be called 237 * before this object, or with a lower priority number to be called after 238 * the listeners were notified. 239 * 240 * All listeners are supposed to have register by now, either in their 241 * readObject or validateObject methods. Notify them that this dataset has 242 * changed. 243 * 244 * @exception InvalidObjectException If the object cannot validate itself. 245 */ 246 public void validateObject() throws InvalidObjectException { 247 fireDatasetChanged(); 248 } 249 250 }