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     * StrokeMap.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     * 27-Sep-2006 : Version 1 (DG);
038     *
039     */
040    
041    package org.jfree.chart;
042    
043    import java.awt.Stroke;
044    import java.io.IOException;
045    import java.io.ObjectInputStream;
046    import java.io.ObjectOutputStream;
047    import java.io.Serializable;
048    import java.util.Iterator;
049    import java.util.Map;
050    import java.util.Set;
051    import java.util.TreeMap;
052    
053    import org.jfree.io.SerialUtilities;
054    import org.jfree.util.ObjectUtilities;
055    
056    /**
057     * A storage structure that maps <code>Comparable</code> instances with
058     * <code>Stroke</code> instances.
059     * <br><br>
060     * To support cloning and serialization, you should only use keys that are
061     * cloneable and serializable.  Special handling for the <code>Stroke</code>
062     * instances is included in this class.
063     *
064     * @since 1.0.3
065     */
066    public class StrokeMap implements Cloneable, Serializable {
067    
068        /** For serialization. */
069        static final long serialVersionUID = -8148916785963525169L;
070    
071        /** Storage for the keys and values. */
072        private transient Map store;
073    
074        /**
075         * Creates a new (empty) map.
076         */
077        public StrokeMap() {
078            this.store = new TreeMap();
079        }
080    
081        /**
082         * Returns the stroke associated with the specified key, or
083         * <code>null</code>.
084         *
085         * @param key  the key (<code>null</code> not permitted).
086         *
087         * @return The stroke, or <code>null</code>.
088         *
089         * @throws IllegalArgumentException if <code>key</code> is
090         *     <code>null</code>.
091         */
092        public Stroke getStroke(Comparable key) {
093            if (key == null) {
094                throw new IllegalArgumentException("Null 'key' argument.");
095            }
096            return (Stroke) this.store.get(key);
097        }
098    
099        /**
100         * Returns <code>true</code> if the map contains the specified key, and
101         * <code>false</code> otherwise.
102         *
103         * @param key  the key.
104         *
105         * @return <code>true</code> if the map contains the specified key, and
106         * <code>false</code> otherwise.
107         */
108        public boolean containsKey(Comparable key) {
109            return this.store.containsKey(key);
110        }
111    
112        /**
113         * Adds a mapping between the specified <code>key</code> and
114         * <code>stroke</code> values.
115         *
116         * @param key  the key (<code>null</code> not permitted).
117         * @param stroke  the stroke.
118         */
119        public void put(Comparable key, Stroke stroke) {
120            if (key == null) {
121                throw new IllegalArgumentException("Null 'key' argument.");
122            }
123            this.store.put(key, stroke);
124        }
125    
126        /**
127         * Resets the map to empty.
128         */
129        public void clear() {
130            this.store.clear();
131        }
132    
133        /**
134         * Tests this map for equality with an arbitrary object.
135         *
136         * @param obj  the object (<code>null</code> permitted).
137         *
138         * @return A boolean.
139         */
140        public boolean equals(Object obj) {
141            if (obj == this) {
142                return true;
143            }
144            if (!(obj instanceof StrokeMap)) {
145                return false;
146            }
147            StrokeMap that = (StrokeMap) obj;
148            if (this.store.size() != that.store.size()) {
149                return false;
150            }
151            Set keys = this.store.keySet();
152            Iterator iterator = keys.iterator();
153            while (iterator.hasNext()) {
154                Comparable key = (Comparable) iterator.next();
155                Stroke s1 = getStroke(key);
156                Stroke s2 = that.getStroke(key);
157                if (!ObjectUtilities.equal(s1, s2)) {
158                    return false;
159                }
160            }
161            return true;
162        }
163    
164        /**
165         * Returns a clone of this <code>StrokeMap</code>.
166         *
167         * @return A clone of this instance.
168         *
169         * @throws CloneNotSupportedException if any key is not cloneable.
170         */
171        public Object clone() throws CloneNotSupportedException {
172            // TODO: I think we need to make sure the keys are actually cloned,
173            // whereas the stroke instances are always immutable so they're OK
174            return super.clone();
175        }
176    
177        /**
178         * Provides serialization support.
179         *
180         * @param stream  the output stream.
181         *
182         * @throws IOException  if there is an I/O error.
183         */
184        private void writeObject(ObjectOutputStream stream) throws IOException {
185            stream.defaultWriteObject();
186            stream.writeInt(this.store.size());
187            Set keys = this.store.keySet();
188            Iterator iterator = keys.iterator();
189            while (iterator.hasNext()) {
190                Comparable key = (Comparable) iterator.next();
191                stream.writeObject(key);
192                Stroke stroke = getStroke(key);
193                SerialUtilities.writeStroke(stroke, stream);
194            }
195        }
196    
197        /**
198         * Provides serialization support.
199         *
200         * @param stream  the input stream.
201         *
202         * @throws IOException  if there is an I/O error.
203         * @throws ClassNotFoundException  if there is a classpath problem.
204         */
205        private void readObject(ObjectInputStream stream)
206                throws IOException, ClassNotFoundException {
207            stream.defaultReadObject();
208            this.store = new TreeMap();
209            int keyCount = stream.readInt();
210            for (int i = 0; i < keyCount; i++) {
211                Comparable key = (Comparable) stream.readObject();
212                Stroke stroke = SerialUtilities.readStroke(stream);
213                this.store.put(key, stroke);
214            }
215        }
216    
217    }