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