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     * AbstractAnnotation.java
029     * -----------------------
030     * (C) Copyright 2009, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  Peter Kolb (see patch 2809117);
033     * Contributor(s):   ;
034     *
035     * Changes:
036     * --------
037     * 20-Jun-2009 : Version 1 (PK);
038     *
039     */
040    
041    package org.jfree.chart.annotations;
042    
043    import java.io.IOException;
044    import java.io.ObjectInputStream;
045    import java.io.ObjectOutputStream;
046    import java.io.Serializable;
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.event.AnnotationChangeEvent;
054    import org.jfree.chart.event.AnnotationChangeListener;
055    
056    /**
057     * An abstract implementation of the {@link Annotation} interface, containing a
058     * mechanism for registering change listeners.
059     *
060     * @since 1.0.14
061     */
062    public abstract class AbstractAnnotation implements Annotation, Cloneable,
063            Serializable {
064    
065        /** Storage for registered change listeners. */
066        private transient EventListenerList listenerList;
067    
068        /**
069         * A flag that indicates whether listeners should be notified
070         * about changes of the annotation.
071         */
072        private boolean notify = true;
073    
074        /**
075         * Constructs an annotation.
076         */
077        protected AbstractAnnotation() {
078            this.listenerList = new EventListenerList();
079        }
080    
081        /**
082         * Registers an object to receive notification of changes to the
083         * annotation.
084         *
085         * @param listener  the object to register.
086         *
087         * @see #removeChangeListener(AnnotationChangeListener)
088         */
089        public void addChangeListener(AnnotationChangeListener listener) {
090            this.listenerList.add(AnnotationChangeListener.class, listener);
091        }
092    
093        /**
094         * Deregisters an object so that it no longer receives notification of
095         * changes to the annotation.
096         *
097         * @param listener  the object to deregister.
098         *
099         * @see #addChangeListener(AnnotationChangeListener)
100         */
101        public void removeChangeListener(AnnotationChangeListener listener) {
102            this.listenerList.remove(AnnotationChangeListener.class, listener);
103        }
104    
105        /**
106         * Returns <code>true</code> if the specified object is registered with
107         * the annotation as a listener.  Most applications won't need to call this
108         * method, it exists mainly for use by unit testing code.
109         *
110         * @param listener  the listener.
111         *
112         * @return A boolean.
113         *
114         * @see #addChangeListener(AnnotationChangeListener)
115         * @see #removeChangeListener(AnnotationChangeListener)
116         */
117        public boolean hasListener(EventListener listener) {
118            List list = Arrays.asList(this.listenerList.getListenerList());
119            return list.contains(listener);
120        }
121    
122        /**
123         * Notifies all registered listeners that the annotation has changed.
124         *
125         * @see #addChangeListener(AnnotationChangeListener)
126         */
127        protected void fireAnnotationChanged() {
128            if (notify) {
129                notifyListeners(new AnnotationChangeEvent(this, this));
130            }
131        }
132    
133        /**
134         * Notifies all registered listeners that the annotation has changed.
135         *
136         * @param event  contains information about the event that triggered the
137         *               notification.
138         *
139         * @see #addChangeListener(AnnotationChangeListener)
140         * @see #removeChangeListener(AnnotationChangeListener)
141         */
142        protected void notifyListeners(AnnotationChangeEvent event) {
143    
144            Object[] listeners = this.listenerList.getListenerList();
145            for (int i = listeners.length - 2; i >= 0; i -= 2) {
146                if (listeners[i] == AnnotationChangeListener.class) {
147                    ((AnnotationChangeListener) listeners[i + 1]).annotationChanged(
148                            event);
149                }
150            }
151    
152        }
153    
154        /**
155         * Returns a flag that indicates whether listeners should be 
156         * notified about changes to the annotation.
157         *
158         * @return  the flag.
159         *
160         * @see #setNotify(boolean)
161         */
162        public boolean getNotify(){
163            return this.notify;
164        }
165    
166        /**
167         * Sets a flag that indicates whether listeners should be notified about
168         * changes of an annotation.
169         *
170         * @param flag  the flag
171         *
172         * @see #getNotify()
173         */
174        public void setNotify(boolean flag){
175            this.notify = flag;
176            if (notify) {
177                fireAnnotationChanged();
178            }
179        }
180    
181        /**
182         * Returns a clone of the annotation. The cloned annotation will NOT 
183         * include the {@link AnnotationChangeListener} references that have been
184         * registered with this annotation.
185         *
186         * @return A clone.
187         *
188         * @throws CloneNotSupportedException  if the annotation does not support
189         *                                     cloning.
190         */
191        public Object clone() throws CloneNotSupportedException {
192            AbstractAnnotation clone = (AbstractAnnotation) super.clone();
193            clone.listenerList = new EventListenerList();
194            return clone;
195        }
196    
197        /**
198         * Handles serialization.
199         *
200         * @param stream  the output stream.
201         *
202         * @throws IOException if there is an I/O problem.
203         */
204        private void writeObject(ObjectOutputStream stream) throws IOException {
205            stream.defaultWriteObject();
206        }
207    
208        /**
209         * Restores a serialized object.
210         *
211         * @param stream  the input stream.
212         *
213         * @throws IOException if there is an I/O problem.
214         * @throws ClassNotFoundException if there is a problem loading a class.
215         */
216        private void readObject(ObjectInputStream stream)
217            throws IOException, ClassNotFoundException {
218            stream.defaultReadObject();
219            this.listenerList = new EventListenerList();
220        }
221    
222    }