    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    }