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    
043    package org.jfree.chart.plot.dial;
044    
045    import java.io.IOException;
046    import java.io.ObjectInputStream;
047    import java.util.Arrays;
048    import java.util.EventListener;
049    import java.util.List;
050    
051    import javax.swing.event.EventListenerList;
052    
053    import 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     */
061    public 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    }