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 * AbstractDialLayer.java
029 * ----------------------
030 * (C) Copyright 2006-2008, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes
036 * -------
037 * 06-Nov-2006 : Version 1 (DG);
038 * 17-Nov-2006 : Added visible flag (DG);
039 * 16-Oct-2007 : Implemented equals() and clone() (DG);
040 *
041 */
042
043package org.jfree.chart.plot.dial;
044
045import java.io.IOException;
046import java.io.ObjectInputStream;
047import java.util.Arrays;
048import java.util.EventListener;
049import java.util.List;
050
051import javax.swing.event.EventListenerList;
052
053import org.jfree.chart.HashUtilities;
054
055/**
056 * A base class that can be used to implement a {@link DialLayer}.  It includes
057 * an event notification mechanism.
058 *
059 * @since 1.0.7
060 */
061public abstract class AbstractDialLayer implements DialLayer {
062
063    /** A flag that controls whether or not the layer is visible. */
064    private boolean visible;
065
066    /** Storage for registered listeners. */
067    private transient EventListenerList listenerList;
068
069    /**
070     * Creates a new instance.
071     */
072    protected AbstractDialLayer() {
073        this.visible = true;
074        this.listenerList = new EventListenerList();
075    }
076
077    /**
078     * Returns <code>true</code> if this layer is visible (should be displayed),
079     * and <code>false</code> otherwise.
080     *
081     * @return A boolean.
082     *
083     * @see #setVisible(boolean)
084     */
085    public boolean isVisible() {
086        return this.visible;
087    }
088
089    /**
090     * Sets the flag that determines whether or not this layer is drawn by
091     * the plot, and sends a {@link DialLayerChangeEvent} to all registered
092     * listeners.
093     *
094     * @param visible  the flag.
095     *
096     * @see #isVisible()
097     */
098    public void setVisible(boolean visible) {
099        this.visible = visible;
100        notifyListeners(new DialLayerChangeEvent(this));
101    }
102
103    /**
104     * Tests this instance for equality with an arbitrary object.
105     *
106     * @param obj  the object (<code>null</code> permitted).
107     *
108     * @return A boolean.
109     */
110    public boolean equals(Object obj) {
111        if (obj == this) {
112            return true;
113        }
114        if (!(obj instanceof AbstractDialLayer)) {
115            return false;
116        }
117        AbstractDialLayer that = (AbstractDialLayer) obj;
118        return this.visible == that.visible;
119    }
120
121    /**
122     * Returns a hash code for this instance.
123     *
124     * @return A hash code.
125     */
126    public int hashCode() {
127        int result = 23;
128        result = HashUtilities.hashCode(result, this.visible);
129        return result;
130    }
131
132    /**
133     * Returns a clone of this instance.
134     *
135     * @return A clone.
136     *
137     * @throws CloneNotSupportedException if there is a problem cloning this
138     *     instance.
139     */
140    public Object clone() throws CloneNotSupportedException {
141        AbstractDialLayer clone = (AbstractDialLayer) super.clone();
142        // we don't clone the listeners
143        clone.listenerList = new EventListenerList();
144        return clone;
145    }
146
147    /**
148     * Registers an object for notification of changes to the dial layer.
149     *
150     * @param listener  the object that is being registered.
151     *
152     * @see #removeChangeListener(DialLayerChangeListener)
153     */
154    public void addChangeListener(DialLayerChangeListener listener) {
155        this.listenerList.add(DialLayerChangeListener.class, listener);
156    }
157
158    /**
159     * Deregisters an object for notification of changes to the dial layer.
160     *
161     * @param listener  the object to deregister.
162     *
163     * @see #addChangeListener(DialLayerChangeListener)
164     */
165    public void removeChangeListener(DialLayerChangeListener listener) {
166        this.listenerList.remove(DialLayerChangeListener.class, listener);
167    }
168
169    /**
170     * Returns <code>true</code> if the specified object is registered with
171     * the dataset as a listener.  Most applications won't need to call this
172     * method, it exists mainly for use by unit testing code.
173     *
174     * @param listener  the listener.
175     *
176     * @return A boolean.
177     */
178    public boolean hasListener(EventListener listener) {
179        List list = Arrays.asList(this.listenerList.getListenerList());
180        return list.contains(listener);
181    }
182
183    /**
184     * Notifies all registered listeners that the dial layer has changed.
185     * The {@link DialLayerChangeEvent} provides information about the change.
186     *
187     * @param event  information about the change to the axis.
188     */
189    protected void notifyListeners(DialLayerChangeEvent event) {
190        Object[] listeners = this.listenerList.getListenerList();
191        for (int i = listeners.length - 2; i >= 0; i -= 2) {
192            if (listeners[i] == DialLayerChangeListener.class) {
193                ((DialLayerChangeListener) listeners[i + 1]).dialLayerChanged(
194                        event);
195            }
196        }
197    }
198
199    /**
200     * Provides serialization support.
201     *
202     * @param stream  the input stream.
203     *
204     * @throws IOException  if there is an I/O error.
205     * @throws ClassNotFoundException  if there is a classpath problem.
206     */
207    private void readObject(ObjectInputStream stream)
208        throws IOException, ClassNotFoundException {
209        stream.defaultReadObject();
210        this.listenerList = new EventListenerList();
211    }
212
213}