    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    }