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