    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 * AbstractDataset.java
    029 * --------------------
    030 * (C)opyright 2000-2008, by Object Refinery Limited.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   Nicolas Brodu (for Astrium and EADS Corporate Research
    034 *                   Center);
    035 *
    036 * Changes (from 21-Aug-2001)
    037 * --------------------------
    038 * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
    039 * 18-Sep-2001 : Updated e-mail address in header (DG);
    040 * 15-Oct-2001 : Moved to new package (com.jrefinery.data.*) (DG);
    041 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
    042 * 17-Nov-2001 : Changed constructor from public to protected, created new
    043 *               AbstractSeriesDataset class and transferred series-related
    044 *               methods, updated Javadoc comments (DG);
    045 * 04-Mar-2002 : Updated import statements (DG);
    046 * 11-Jun-2002 : Updated for change in the event constructor (DG);
    047 * 07-Aug-2002 : Changed listener list to use
    048 *               javax.swing.event.EventListenerList (DG);
    049 * 04-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    050 * 27-Mar-2003 : Implemented Serializable (DG);
    051 * 18-Aug-2003 : Implemented Cloneable (DG);
    052 * 08-Sep-2003 : Serialization fixes (NB);
    053 * 11-Sep-2003 : Cloning Fixes (NB);
    054 * 01-Jun-2005 : Added hasListener() method for unit testing (DG);
    055 *
    056 */
    057
    058package org.jfree.data.general;
    059
    060import java.io.IOException;
    061import java.io.InvalidObjectException;
    062import java.io.ObjectInputStream;
    063import java.io.ObjectInputValidation;
    064import java.io.ObjectOutputStream;
    065import java.io.Serializable;
    066import java.util.Arrays;
    067import java.util.EventListener;
    068import java.util.List;
    069
    070import javax.swing.event.EventListenerList;
    071
    072/**
    073 * An abstract implementation of the {@link Dataset} interface, containing a
    074 * mechanism for registering change listeners.
    075 */
    076public abstract class AbstractDataset implements Dataset, Cloneable,
    077        Serializable, ObjectInputValidation {
    078
    079    /** For serialization. */
    080    private static final long serialVersionUID = 1918768939869230744L;
    081
    082    /** The group that the dataset belongs to. */
    083    private DatasetGroup group;
    084
    085    /** Storage for registered change listeners. */
    086    private transient EventListenerList listenerList;
    087
    088    /**
    089     * Constructs a dataset. By default, the dataset is assigned to its own
    090     * group.
    091     */
    092    protected AbstractDataset() {
    093        this.group = new DatasetGroup();
    094        this.listenerList = new EventListenerList();
    095    }
    096
    097    /**
    098     * Returns the dataset group for the dataset.
    099     *
    100     * @return The group (never <code>null</code>).
    101     *
    102     * @see #setGroup(DatasetGroup)
    103     */
    104    public DatasetGroup getGroup() {
    105        return this.group;
    106    }
    107
    108    /**
    109     * Sets the dataset group for the dataset.
    110     *
    111     * @param group  the group (<code>null</code> not permitted).
    112     *
    113     * @see #getGroup()
    114     */
    115    public void setGroup(DatasetGroup group) {
    116        if (group == null) {
    117            throw new IllegalArgumentException("Null 'group' argument.");
    118        }
    119        this.group = group;
    120    }
    121
    122    /**
    123     * Registers an object to receive notification of changes to the dataset.
    124     *
    125     * @param listener  the object to register.
    126     *
    127     * @see #removeChangeListener(DatasetChangeListener)
    128     */
    129    public void addChangeListener(DatasetChangeListener listener) {
    130        this.listenerList.add(DatasetChangeListener.class, listener);
    131    }
    132
    133    /**
    134     * Deregisters an object so that it no longer receives notification of
    135     * changes to the dataset.
    136     *
    137     * @param listener  the object to deregister.
    138     *
    139     * @see #addChangeListener(DatasetChangeListener)
    140     */
    141    public void removeChangeListener(DatasetChangeListener listener) {
    142        this.listenerList.remove(DatasetChangeListener.class, listener);
    143    }
    144
    145    /**
    146     * Returns <code>true</code> if the specified object is registered with
    147     * the dataset as a listener.  Most applications won't need to call this
    148     * method, it exists mainly for use by unit testing code.
    149     *
    150     * @param listener  the listener.
    151     *
    152     * @return A boolean.
    153     *
    154     * @see #addChangeListener(DatasetChangeListener)
    155     * @see #removeChangeListener(DatasetChangeListener)
    156     */
    157    public boolean hasListener(EventListener listener) {
    158        List list = Arrays.asList(this.listenerList.getListenerList());
    159        return list.contains(listener);
    160    }
    161
    162    /**
    163     * Notifies all registered listeners that the dataset has changed.
    164     *
    165     * @see #addChangeListener(DatasetChangeListener)
    166     */
    167    protected void fireDatasetChanged() {
    168        notifyListeners(new DatasetChangeEvent(this, this));
    169    }
    170
    171    /**
    172     * Notifies all registered listeners that the dataset has changed.
    173     *
    174     * @param event  contains information about the event that triggered the
    175     *               notification.
    176     *
    177     * @see #addChangeListener(DatasetChangeListener)
    178     * @see #removeChangeListener(DatasetChangeListener)
    179     */
    180    protected void notifyListeners(DatasetChangeEvent event) {
    181
    182        Object[] listeners = this.listenerList.getListenerList();
    183        for (int i = listeners.length - 2; i >= 0; i -= 2) {
    184            if (listeners[i] == DatasetChangeListener.class) {
    185                ((DatasetChangeListener) listeners[i + 1]).datasetChanged(
    186                        event);
    187            }
    188        }
    189
    190    }
    191
    192    /**
    193     * Returns a clone of the dataset. The cloned dataset will NOT include the
    194     * {@link DatasetChangeListener} references that have been registered with
    195     * this dataset.
    196     *
    197     * @return A clone.
    198     *
    199     * @throws CloneNotSupportedException  if the dataset does not support
    200     *                                     cloning.
    201     */
    202    public Object clone() throws CloneNotSupportedException {
    203        AbstractDataset clone = (AbstractDataset) super.clone();
    204        clone.listenerList = new EventListenerList();
    205        return clone;
    206    }
    207
    208    /**
    209     * Handles serialization.
    210     *
    211     * @param stream  the output stream.
    212     *
    213     * @throws IOException if there is an I/O problem.
    214     */
    215    private void writeObject(ObjectOutputStream stream) throws IOException {
    216        stream.defaultWriteObject();
    217    }
    218
    219    /**
    220     * Restores a serialized object.
    221     *
    222     * @param stream  the input stream.
    223     *
    224     * @throws IOException if there is an I/O problem.
    225     * @throws ClassNotFoundException if there is a problem loading a class.
    226     */
    227    private void readObject(ObjectInputStream stream)
    228        throws IOException, ClassNotFoundException {
    229        stream.defaultReadObject();
    230        this.listenerList = new EventListenerList();
    231        stream.registerValidation(this, 10);  // see comments about priority of
    232                                              // 10 in validateObject()
    233    }
    234
    235    /**
    236     * Validates the object. We use this opportunity to call listeners who have
    237     * registered during the deserialization process, as listeners are not
    238     * serialized. This method is called by the serialization system after the
    239     * entire graph is read.
    240     *
    241     * This object has registered itself to the system with a priority of 10.
    242     * Other callbacks may register with a higher priority number to be called
    243     * before this object, or with a lower priority number to be called after
    244     * the listeners were notified.
    245     *
    246     * All listeners are supposed to have register by now, either in their
    247     * readObject or validateObject methods. Notify them that this dataset has
    248     * changed.
    249     *
    250     * @exception InvalidObjectException If the object cannot validate itself.
    251     */
    252    public void validateObject() throws InvalidObjectException {
    253        fireDatasetChanged();
    254    }
    255
    256}