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