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 * PaintMap.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.Paint;
040import java.io.IOException;
041import java.io.ObjectInputStream;
042import java.io.ObjectOutputStream;
043import java.io.Serializable;
044import java.util.HashMap;
045import java.util.Iterator;
046import java.util.Map;
047import java.util.Set;
048import org.jfree.chart.util.PaintUtils;
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 Paint} 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 Paint}
058 * instances is included in this class.
059 *
060 * @since 1.0.3
061 */
062public class PaintMap implements Cloneable, Serializable {
063
064    /** For serialization. */
065    static final long serialVersionUID = -4639833772123069274L;
066
067    /** Storage for the keys and values. */
068    private transient Map store;
069
070    /**
071     * Creates a new (empty) map.
072     */
073    public PaintMap() {
074        this.store = new HashMap();
075    }
076
077    /**
078     * Returns the paint associated with the specified key, or
079     * {@code null}.
080     *
081     * @param key  the key ({@code null} not permitted).
082     *
083     * @return The paint, or {@code null}.
084     *
085     * @throws IllegalArgumentException if {@code key} is
086     *     {@code null}.
087     */
088    public Paint getPaint(Comparable key) {
089        Args.nullNotPermitted(key, "key");
090        return (Paint) 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 Paint} values.
109     *
110     * @param key  the key ({@code null} not permitted).
111     * @param paint  the paint.
112     *
113     * @throws IllegalArgumentException if {@code key} is
114     *     {@code null}.
115     */
116    public void put(Comparable key, Paint paint) {
117        Args.nullNotPermitted(key, "key");
118        this.store.put(key, paint);
119    }
120
121    /**
122     * Resets the map to empty.
123     */
124    public void clear() {
125        this.store.clear();
126    }
127
128    /**
129     * Tests this map for equality with an arbitrary object.
130     *
131     * @param obj  the object ({@code null} permitted).
132     *
133     * @return A boolean.
134     */
135    @Override
136    public boolean equals(Object obj) {
137        if (obj == this) {
138            return true;
139        }
140        if (!(obj instanceof PaintMap)) {
141            return false;
142        }
143        PaintMap that = (PaintMap) obj;
144        if (this.store.size() != that.store.size()) {
145            return false;
146        }
147        Set keys = this.store.keySet();
148        Iterator iterator = keys.iterator();
149        while (iterator.hasNext()) {
150            Comparable key = (Comparable) iterator.next();
151            Paint p1 = getPaint(key);
152            Paint p2 = that.getPaint(key);
153            if (!PaintUtils.equal(p1, p2)) {
154                return false;
155            }
156        }
157        return true;
158    }
159
160    /**
161     * Returns a clone of this {@code PaintMap}.
162     *
163     * @return A clone of this instance.
164     *
165     * @throws CloneNotSupportedException if any key is not cloneable.
166     */
167    @Override
168    public Object clone() throws CloneNotSupportedException {
169        PaintMap clone = (PaintMap) super.clone();
170        clone.store = new HashMap();
171        clone.store.putAll(this.store);
172        // TODO: I think we need to make sure the keys are actually cloned,
173        // whereas the paint instances are always immutable so they're OK
174        return 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            Paint paint = getPaint(key);
193            SerialUtils.writePaint(paint, 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 HashMap();
209        int keyCount = stream.readInt();
210        for (int i = 0; i < keyCount; i++) {
211            Comparable key = (Comparable) stream.readObject();
212            Paint paint = SerialUtils.readPaint(stream);
213            this.store.put(key, paint);
214        }
215    }
216
217}