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