001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, 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 }