001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2011, 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 * [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 * Series.java 029 * ----------- 030 * (C) Copyright 2001-2011, 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 * 16-Oct-2011 : Added vetoable property change support for series name (DG); 053 */ 054 055package org.jfree.data.general; 056 057import java.beans.PropertyChangeListener; 058import java.beans.PropertyChangeSupport; 059import java.beans.PropertyVetoException; 060import java.beans.VetoableChangeListener; 061import java.beans.VetoableChangeSupport; 062import java.io.Serializable; 063 064import javax.swing.event.EventListenerList; 065 066import org.jfree.chart.util.ParamChecks; 067import org.jfree.util.ObjectUtilities; 068 069/** 070 * Base class representing a data series. Subclasses are left to implement the 071 * actual data structures. 072 * <P> 073 * The series has two properties ("Key" and "Description") for which you can 074 * register a <code>PropertyChangeListener</code>. 075 * <P> 076 * You can also register a {@link SeriesChangeListener} to receive notification 077 * of changes to the series data. 078 */ 079public abstract class Series implements Cloneable, Serializable { 080 081 /** For serialization. */ 082 private static final long serialVersionUID = -6906561437538683581L; 083 084 /** The key for the series. */ 085 private Comparable key; 086 087 /** A description of the series. */ 088 private String description; 089 090 /** Storage for registered change listeners. */ 091 private EventListenerList listeners; 092 093 /** Object to support property change notification. */ 094 private PropertyChangeSupport propertyChangeSupport; 095 096 /** Object to support property change notification. */ 097 private VetoableChangeSupport vetoableChangeSupport; 098 099 /** A flag that controls whether or not changes are notified. */ 100 private boolean notify; 101 102 /** 103 * Creates a new series with the specified key. 104 * 105 * @param key the series key (<code>null</code> not permitted). 106 */ 107 protected Series(Comparable key) { 108 this(key, null); 109 } 110 111 /** 112 * Creates a new series with the specified key and description. 113 * 114 * @param key the series key (<code>null</code> NOT permitted). 115 * @param description the series description (<code>null</code> permitted). 116 */ 117 protected Series(Comparable key, String description) { 118 if (key == null) { 119 throw new IllegalArgumentException("Null 'key' argument."); 120 } 121 this.key = key; 122 this.description = description; 123 this.listeners = new EventListenerList(); 124 this.propertyChangeSupport = new PropertyChangeSupport(this); 125 this.vetoableChangeSupport = new VetoableChangeSupport(this); 126 this.notify = true; 127 } 128 129 /** 130 * Returns the key for the series. 131 * 132 * @return The series key (never <code>null</code>). 133 * 134 * @see #setKey(Comparable) 135 */ 136 public Comparable getKey() { 137 return this.key; 138 } 139 140 /** 141 * Sets the key for the series and sends a <code>VetoableChangeEvent</code> 142 * (with the property name "Key") to all registered listeners. For 143 * backwards compatibility, this method also fires a regular 144 * <code>PropertyChangeEvent</code>. 145 * 146 * @param key the key (<code>null</code> not permitted). 147 * 148 * @see #getKey() 149 */ 150 public void setKey(Comparable key) { 151 ParamChecks.nullNotPermitted(key, "key"); 152 Comparable old = this.key; 153 try { 154 // if this series belongs to a dataset, the dataset might veto the 155 // change if it results in two series within the dataset having the 156 // same key 157 this.vetoableChangeSupport.fireVetoableChange("Key", old, key); 158 this.key = key; 159 // prior to 1.0.14, we just fired a PropertyChange - so we need to 160 // keep doing this 161 this.propertyChangeSupport.firePropertyChange("Key", old, key); 162 } catch (PropertyVetoException e) { 163 throw new IllegalArgumentException(e.getMessage()); 164 } 165 } 166 167 /** 168 * Returns a description of the series. 169 * 170 * @return The series description (possibly <code>null</code>). 171 * 172 * @see #setDescription(String) 173 */ 174 public String getDescription() { 175 return this.description; 176 } 177 178 /** 179 * Sets the description of the series and sends a 180 * <code>PropertyChangeEvent</code> to all registered listeners. 181 * 182 * @param description the description (<code>null</code> permitted). 183 * 184 * @see #getDescription() 185 */ 186 public void setDescription(String description) { 187 String old = this.description; 188 this.description = description; 189 this.propertyChangeSupport.firePropertyChange("Description", old, 190 description); 191 } 192 193 /** 194 * Returns the flag that controls whether or not change events are sent to 195 * registered listeners. 196 * 197 * @return A boolean. 198 * 199 * @see #setNotify(boolean) 200 */ 201 public boolean getNotify() { 202 return this.notify; 203 } 204 205 /** 206 * Sets the flag that controls whether or not change events are sent to 207 * registered listeners. 208 * 209 * @param notify the new value of the flag. 210 * 211 * @see #getNotify() 212 */ 213 public void setNotify(boolean notify) { 214 if (this.notify != notify) { 215 this.notify = notify; 216 fireSeriesChanged(); 217 } 218 } 219 220 /** 221 * Returns <code>true</code> if the series contains no data items, and 222 * <code>false</code> otherwise. 223 * 224 * @return A boolean. 225 * 226 * @since 1.0.7 227 */ 228 public boolean isEmpty() { 229 return (getItemCount() == 0); 230 } 231 232 /** 233 * Returns the number of data items in the series. 234 * 235 * @return The number of data items in the series. 236 */ 237 public abstract int getItemCount(); 238 239 /** 240 * Returns a clone of the series. 241 * <P> 242 * Notes: 243 * <ul> 244 * <li>No need to clone the name or description, since String object is 245 * immutable.</li> 246 * <li>We set the listener list to empty, since the listeners did not 247 * register with the clone.</li> 248 * <li>Same applies to the PropertyChangeSupport instance.</li> 249 * </ul> 250 * 251 * @return A clone of the series. 252 * 253 * @throws CloneNotSupportedException not thrown by this class, but 254 * subclasses may differ. 255 */ 256 public Object clone() throws CloneNotSupportedException { 257 Series clone = (Series) super.clone(); 258 clone.listeners = new EventListenerList(); 259 clone.propertyChangeSupport = new PropertyChangeSupport(clone); 260 clone.vetoableChangeSupport = new VetoableChangeSupport(clone); 261 return clone; 262 } 263 264 /** 265 * Tests the series for equality with another object. 266 * 267 * @param obj the object (<code>null</code> permitted). 268 * 269 * @return <code>true</code> or <code>false</code>. 270 */ 271 public boolean equals(Object obj) { 272 if (obj == this) { 273 return true; 274 } 275 if (!(obj instanceof Series)) { 276 return false; 277 } 278 Series that = (Series) obj; 279 if (!getKey().equals(that.getKey())) { 280 return false; 281 } 282 if (!ObjectUtilities.equal(getDescription(), that.getDescription())) { 283 return false; 284 } 285 return true; 286 } 287 288 /** 289 * Returns a hash code. 290 * 291 * @return A hash code. 292 */ 293 public int hashCode() { 294 int result; 295 result = this.key.hashCode(); 296 result = 29 * result + (this.description != null 297 ? this.description.hashCode() : 0); 298 return result; 299 } 300 301 /** 302 * Registers an object with this series, to receive notification whenever 303 * the series changes. 304 * <P> 305 * Objects being registered must implement the {@link SeriesChangeListener} 306 * interface. 307 * 308 * @param listener the listener to register. 309 */ 310 public void addChangeListener(SeriesChangeListener listener) { 311 this.listeners.add(SeriesChangeListener.class, listener); 312 } 313 314 /** 315 * Deregisters an object, so that it not longer receives notification 316 * whenever the series changes. 317 * 318 * @param listener the listener to deregister. 319 */ 320 public void removeChangeListener(SeriesChangeListener listener) { 321 this.listeners.remove(SeriesChangeListener.class, listener); 322 } 323 324 /** 325 * General method for signalling to registered listeners that the series 326 * has been changed. 327 */ 328 public void fireSeriesChanged() { 329 if (this.notify) { 330 notifyListeners(new SeriesChangeEvent(this)); 331 } 332 } 333 334 /** 335 * Sends a change event to all registered listeners. 336 * 337 * @param event contains information about the event that triggered the 338 * notification. 339 */ 340 protected void notifyListeners(SeriesChangeEvent event) { 341 342 Object[] listenerList = this.listeners.getListenerList(); 343 for (int i = listenerList.length - 2; i >= 0; i -= 2) { 344 if (listenerList[i] == SeriesChangeListener.class) { 345 ((SeriesChangeListener) listenerList[i + 1]).seriesChanged( 346 event); 347 } 348 } 349 350 } 351 352 /** 353 * Adds a property change listener to the series. 354 * 355 * @param listener the listener. 356 */ 357 public void addPropertyChangeListener(PropertyChangeListener listener) { 358 this.propertyChangeSupport.addPropertyChangeListener(listener); 359 } 360 361 /** 362 * Removes a property change listener from the series. 363 * 364 * @param listener the listener. 365 */ 366 public void removePropertyChangeListener(PropertyChangeListener listener) { 367 this.propertyChangeSupport.removePropertyChangeListener(listener); 368 } 369 370 /** 371 * Fires a property change event. 372 * 373 * @param property the property key. 374 * @param oldValue the old value. 375 * @param newValue the new value. 376 */ 377 protected void firePropertyChange(String property, Object oldValue, 378 Object newValue) { 379 this.propertyChangeSupport.firePropertyChange(property, oldValue, 380 newValue); 381 } 382 383 /** 384 * Adds a vetoable property change listener to the series. 385 * 386 * @param listener the listener. 387 * 388 * @since 1.0.14 389 */ 390 public void addVetoableChangeListener(VetoableChangeListener listener) { 391 this.vetoableChangeSupport.addVetoableChangeListener(listener); 392 } 393 394 /** 395 * Removes a vetoable property change listener from the series. 396 * 397 * @param listener the listener. 398 * 399 * @since 1.0.14 400 */ 401 public void removeVetoableChangeListener(VetoableChangeListener listener) { 402 this.vetoableChangeSupport.removeVetoableChangeListener(listener); 403 } 404 405 /** 406 * Fires a vetoable property change event. 407 * 408 * @param property the property key. 409 * @param oldValue the old value. 410 * @param newValue the new value. 411 */ 412 protected void fireVetoableChange(String property, Object oldValue, 413 Object newValue) throws PropertyVetoException { 414 this.vetoableChangeSupport.fireVetoableChange(property, oldValue, 415 newValue); 416 } 417 418}