001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2006, 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 * Series.java
029 * -----------
030 * (C) Copyright 2001-2006, by Object Refinery Limited.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): -;
034 *
035 * $Id: Series.java,v 1.9.2.3 2006/07/25 15:55:48 mungady Exp $
036 *
037 * Changes
038 * -------
039 * 15-Nov-2001 : Version 1 (DG);
040 * 29-Nov-2001 : Added cloning and property change support (DG);
041 * 30-Jan-2002 : Added a description attribute and changed the constructors to
042 * protected (DG);
043 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
044 * 13-Mar-2003 : Implemented Serializable (DG);
045 * 01-May-2003 : Added equals() method (DG);
046 * 26-Jun-2003 : Changed listener list to use EventListenerList - see bug
047 * 757027 (DG);
048 * 15-Oct-2003 : Added a flag to control whether or not change events are sent
049 * to registered listeners (DG);
050 * 19-May-2005 : Made abstract (DG);
051 * ------------- JFREECHART 1.0.0 ---------------------------------------------
052 * 04-May-2006 : Updated API docs (DG);
053 *
054 */
055
056 package org.jfree.data.general;
057
058 import java.beans.PropertyChangeListener;
059 import java.beans.PropertyChangeSupport;
060 import java.io.Serializable;
061
062 import javax.swing.event.EventListenerList;
063
064 import org.jfree.util.ObjectUtilities;
065
066 /**
067 * Base class representing a data series. Subclasses are left to implement the
068 * actual data structures.
069 * <P>
070 * The series has two properties ("Key" and "Description") for which you can
071 * register a <code>PropertyChangeListener</code>.
072 * <P>
073 * You can also register a {@link SeriesChangeListener} to receive notification
074 * of changes to the series data.
075 */
076 public abstract class Series implements Cloneable, Serializable {
077
078 /** For serialization. */
079 private static final long serialVersionUID = -6906561437538683581L;
080
081 /** The key for the series. */
082 private Comparable key;
083
084 /** A description of the series. */
085 private String description;
086
087 /** Storage for registered change listeners. */
088 private EventListenerList listeners;
089
090 /** Object to support property change notification. */
091 private PropertyChangeSupport propertyChangeSupport;
092
093 /** A flag that controls whether or not changes are notified. */
094 private boolean notify;
095
096 /**
097 * Creates a new series with the specified key.
098 *
099 * @param key the series key (<code>null</code> not permitted).
100 */
101 protected Series(Comparable key) {
102 this(key, null);
103 }
104
105 /**
106 * Creates a new series with the specified key and description.
107 *
108 * @param key the series key (<code>null</code> NOT permitted).
109 * @param description the series description (<code>null</code> permitted).
110 */
111 protected Series(Comparable key, String description) {
112 if (key == null) {
113 throw new IllegalArgumentException("Null 'key' argument.");
114 }
115 this.key = key;
116 this.description = description;
117 this.listeners = new EventListenerList();
118 this.propertyChangeSupport = new PropertyChangeSupport(this);
119 this.notify = true;
120 }
121
122 /**
123 * Returns the key for the series.
124 *
125 * @return The series key (never <code>null</code>).
126 *
127 * @see #setKey(Comparable)
128 */
129 public Comparable getKey() {
130 return this.key;
131 }
132
133 /**
134 * Sets the key for the series and sends a <code>PropertyChangeEvent</code>
135 * (with the property name "Key") to all registered listeners.
136 *
137 * @param key the key (<code>null</code> not permitted).
138 *
139 * @see #getKey()
140 */
141 public void setKey(Comparable key) {
142 if (key == null) {
143 throw new IllegalArgumentException("Null 'key' argument.");
144 }
145 Comparable old = this.key;
146 this.key = key;
147 this.propertyChangeSupport.firePropertyChange("Key", old, key);
148 }
149
150 /**
151 * Returns a description of the series.
152 *
153 * @return The series description (possibly <code>null</code>).
154 *
155 * @see #setDescription(String)
156 */
157 public String getDescription() {
158 return this.description;
159 }
160
161 /**
162 * Sets the description of the series and sends a
163 * <code>PropertyChangeEvent</code> to all registered listeners.
164 *
165 * @param description the description (<code>null</code> permitted).
166 *
167 * @see #getDescription()
168 */
169 public void setDescription(String description) {
170 String old = this.description;
171 this.description = description;
172 this.propertyChangeSupport.firePropertyChange("Description", old,
173 description);
174 }
175
176 /**
177 * Returns the flag that controls whether or not change events are sent to
178 * registered listeners.
179 *
180 * @return A boolean.
181 *
182 * @see #setNotify(boolean)
183 */
184 public boolean getNotify() {
185 return this.notify;
186 }
187
188 /**
189 * Sets the flag that controls whether or not change events are sent to
190 * registered listeners.
191 *
192 * @param notify the new value of the flag.
193 *
194 * @see #getNotify()
195 */
196 public void setNotify(boolean notify) {
197 if (this.notify != notify) {
198 this.notify = notify;
199 fireSeriesChanged();
200 }
201 }
202
203 /**
204 * Returns a clone of the series.
205 * <P>
206 * Notes:
207 * <ul>
208 * <li>No need to clone the name or description, since String object is
209 * immutable.</li>
210 * <li>We set the listener list to empty, since the listeners did not
211 * register with the clone.</li>
212 * <li>Same applies to the PropertyChangeSupport instance.</li>
213 * </ul>
214 *
215 * @return A clone of the series.
216 *
217 * @throws CloneNotSupportedException not thrown by this class, but
218 * subclasses may differ.
219 */
220 public Object clone() throws CloneNotSupportedException {
221
222 Series clone = (Series) super.clone();
223 clone.listeners = new EventListenerList();
224 clone.propertyChangeSupport = new PropertyChangeSupport(clone);
225 return clone;
226
227 }
228
229 /**
230 * Tests the series for equality with another object.
231 *
232 * @param obj the object (<code>null</code> permitted).
233 *
234 * @return <code>true</code> or <code>false</code>.
235 */
236 public boolean equals(Object obj) {
237 if (obj == this) {
238 return true;
239 }
240 if (!(obj instanceof Series)) {
241 return false;
242 }
243 Series that = (Series) obj;
244 if (!getKey().equals(that.getKey())) {
245 return false;
246 }
247 if (!ObjectUtilities.equal(getDescription(), that.getDescription())) {
248 return false;
249 }
250 return true;
251 }
252
253 /**
254 * Returns a hash code.
255 *
256 * @return A hash code.
257 */
258 public int hashCode() {
259 int result;
260 result = this.key.hashCode();
261 result = 29 * result + (this.description != null
262 ? this.description.hashCode() : 0);
263 return result;
264 }
265
266 /**
267 * Registers an object with this series, to receive notification whenever
268 * the series changes.
269 * <P>
270 * Objects being registered must implement the {@link SeriesChangeListener}
271 * interface.
272 *
273 * @param listener the listener to register.
274 */
275 public void addChangeListener(SeriesChangeListener listener) {
276 this.listeners.add(SeriesChangeListener.class, listener);
277 }
278
279 /**
280 * Deregisters an object, so that it not longer receives notification
281 * whenever the series changes.
282 *
283 * @param listener the listener to deregister.
284 */
285 public void removeChangeListener(SeriesChangeListener listener) {
286 this.listeners.remove(SeriesChangeListener.class, listener);
287 }
288
289 /**
290 * General method for signalling to registered listeners that the series
291 * has been changed.
292 */
293 public void fireSeriesChanged() {
294 if (this.notify) {
295 notifyListeners(new SeriesChangeEvent(this));
296 }
297 }
298
299 /**
300 * Sends a change event to all registered listeners.
301 *
302 * @param event contains information about the event that triggered the
303 * notification.
304 */
305 protected void notifyListeners(SeriesChangeEvent event) {
306
307 Object[] listenerList = this.listeners.getListenerList();
308 for (int i = listenerList.length - 2; i >= 0; i -= 2) {
309 if (listenerList[i] == SeriesChangeListener.class) {
310 ((SeriesChangeListener) listenerList[i + 1]).seriesChanged(
311 event);
312 }
313 }
314
315 }
316
317 /**
318 * Adds a property change listener to the series.
319 *
320 * @param listener the listener.
321 */
322 public void addPropertyChangeListener(PropertyChangeListener listener) {
323 this.propertyChangeSupport.addPropertyChangeListener(listener);
324 }
325
326 /**
327 * Removes a property change listener from the series.
328 *
329 * @param listener The listener.
330 */
331 public void removePropertyChangeListener(PropertyChangeListener listener) {
332 this.propertyChangeSupport.removePropertyChangeListener(listener);
333 }
334
335 /**
336 * Fires a property change event.
337 *
338 * @param property the property key.
339 * @param oldValue the old value.
340 * @param newValue the new value.
341 */
342 protected void firePropertyChange(String property, Object oldValue,
343 Object newValue) {
344 this.propertyChangeSupport.firePropertyChange(property, oldValue,
345 newValue);
346 }
347
348 }