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     * LookupPaintScale.java
029     * ---------------------
030     * (C) Copyright 2006-2009, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * Changes
036     * -------
037     * 05-Jul-2006 : Version 1 (DG);
038     * 31-Jan-2007 : Fixed serialization support (DG);
039     * 09-Mar-2007 : Fixed cloning (DG);
040     * 14-Jun-2007 : Use double primitive in PaintItem (DG);
041     * 28-Mar-2009 : Made PaintItem inner class static (DG);
042     *
043     */
044    
045    package org.jfree.chart.renderer;
046    
047    import java.awt.Color;
048    import java.awt.Paint;
049    import java.io.IOException;
050    import java.io.ObjectInputStream;
051    import java.io.ObjectOutputStream;
052    import java.io.Serializable;
053    import java.util.Collections;
054    import java.util.List;
055    
056    import org.jfree.io.SerialUtilities;
057    import org.jfree.util.PaintUtilities;
058    import org.jfree.util.PublicCloneable;
059    
060    /**
061     * A paint scale that uses a lookup table to associate paint instances
062     * with data value ranges.
063     *
064     * @since 1.0.4
065     */
066    public class LookupPaintScale
067            implements PaintScale, PublicCloneable, Serializable {
068    
069        /**
070         * Stores the paint for a value.
071         */
072        static class PaintItem implements Comparable, Serializable {
073    
074            /** For serialization. */
075            static final long serialVersionUID = 698920578512361570L;
076    
077            /** The value. */
078            double value;
079    
080            /** The paint. */
081            transient Paint paint;
082    
083            /**
084             * Creates a new instance.
085             *
086             * @param value  the value.
087             * @param paint  the paint.
088             */
089            public PaintItem(double value, Paint paint) {
090                this.value = value;
091                this.paint = paint;
092            }
093    
094            /**
095             * Compares this item to an arbitrary object.
096             *
097             * @param obj  the object.
098             *
099             * @return An int defining the relative order of the objects.
100             */
101            public int compareTo(Object obj) {
102                PaintItem that = (PaintItem) obj;
103                double d1 = this.value;
104                double d2 = that.value;
105                if (d1 > d2) {
106                    return 1;
107                }
108                if (d1 < d2) {
109                    return -1;
110                }
111                return 0;
112            }
113    
114            /**
115             * Tests this item for equality with an arbitrary object.
116             *
117             * @param obj  the object (<code>null</code> permitted).
118             *
119             * @return A boolean.
120             */
121            public boolean equals(Object obj) {
122                if (obj == this) {
123                    return true;
124                }
125                if (!(obj instanceof PaintItem)) {
126                    return false;
127                }
128                PaintItem that = (PaintItem) obj;
129                if (this.value != that.value) {
130                    return false;
131                }
132                if (!PaintUtilities.equal(this.paint, that.paint)) {
133                    return false;
134                }
135                return true;
136            }
137    
138            /**
139             * Provides serialization support.
140             *
141             * @param stream  the output stream.
142             *
143             * @throws IOException  if there is an I/O error.
144             */
145            private void writeObject(ObjectOutputStream stream) throws IOException {
146                stream.defaultWriteObject();
147                SerialUtilities.writePaint(this.paint, stream);
148            }
149    
150            /**
151             * Provides serialization support.
152             *
153             * @param stream  the input stream.
154             *
155             * @throws IOException  if there is an I/O error.
156             * @throws ClassNotFoundException  if there is a classpath problem.
157             */
158            private void readObject(ObjectInputStream stream)
159                    throws IOException, ClassNotFoundException {
160                stream.defaultReadObject();
161                this.paint = SerialUtilities.readPaint(stream);
162            }
163    
164        }
165    
166        /** For serialization. */
167        static final long serialVersionUID = -5239384246251042006L;
168    
169        /** The lower bound. */
170        private double lowerBound;
171    
172        /** The upper bound. */
173        private double upperBound;
174    
175        /** The default paint. */
176        private transient Paint defaultPaint;
177    
178        /** The lookup table. */
179        private List lookupTable;
180    
181        /**
182         * Creates a new paint scale.
183         */
184        public LookupPaintScale() {
185            this(0.0, 1.0, Color.lightGray);
186        }
187    
188        /**
189         * Creates a new paint scale with the specified default paint.
190         *
191         * @param lowerBound  the lower bound.
192         * @param upperBound  the upper bound.
193         * @param defaultPaint  the default paint (<code>null</code> not
194         *     permitted).
195         */
196        public LookupPaintScale(double lowerBound, double upperBound,
197                Paint defaultPaint) {
198            if (lowerBound >= upperBound) {
199                throw new IllegalArgumentException(
200                        "Requires lowerBound < upperBound.");
201            }
202            if (defaultPaint == null) {
203                throw new IllegalArgumentException("Null 'paint' argument.");
204            }
205            this.lowerBound = lowerBound;
206            this.upperBound = upperBound;
207            this.defaultPaint = defaultPaint;
208            this.lookupTable = new java.util.ArrayList();
209        }
210    
211        /**
212         * Returns the default paint (never <code>null</code>).
213         *
214         * @return The default paint.
215         */
216        public Paint getDefaultPaint() {
217            return this.defaultPaint;
218        }
219    
220        /**
221         * Returns the lower bound.
222         *
223         * @return The lower bound.
224         *
225         * @see #getUpperBound()
226         */
227        public double getLowerBound() {
228            return this.lowerBound;
229        }
230    
231        /**
232         * Returns the upper bound.
233         *
234         * @return The upper bound.
235         *
236         * @see #getLowerBound()
237         */
238        public double getUpperBound() {
239            return this.upperBound;
240        }
241    
242        /**
243         * Adds an entry to the lookup table.  Any values from <code>n</code> up
244         * to but not including the next value in the table take on the specified
245         * <code>paint</code>.
246         *
247         * @param value  the data value (<code>null</code> not permitted).
248         * @param paint  the paint.
249         *
250         * @deprecated Use {@link #add(double, Paint)}.
251         */
252        public void add(Number value, Paint paint) {
253            add(value.doubleValue(), paint);
254        }
255    
256        /**
257         * Adds an entry to the lookup table.  Any values from <code>n</code> up
258         * to but not including the next value in the table take on the specified
259         * <code>paint</code>.
260         *
261         * @param value  the data value.
262         * @param paint  the paint.
263         *
264         * @since 1.0.6
265         */
266        public void add(double value, Paint paint) {
267            PaintItem item = new PaintItem(value, paint);
268            int index = Collections.binarySearch(this.lookupTable, item);
269            if (index >= 0) {
270                this.lookupTable.set(index, item);
271            }
272            else {
273                this.lookupTable.add(-(index + 1), item);
274            }
275        }
276    
277        /**
278         * Returns the paint associated with the specified value.
279         *
280         * @param value  the value.
281         *
282         * @return The paint.
283         *
284         * @see #getDefaultPaint()
285         */
286        public Paint getPaint(double value) {
287    
288            // handle value outside bounds...
289            if (value < this.lowerBound) {
290                return this.defaultPaint;
291            }
292            if (value > this.upperBound) {
293                return this.defaultPaint;
294            }
295    
296            int count = this.lookupTable.size();
297            if (count == 0) {
298                return this.defaultPaint;
299            }
300    
301            // handle special case where value is less that item zero
302            PaintItem item = (PaintItem) this.lookupTable.get(0);
303            if (value < item.value) {
304                return this.defaultPaint;
305            }
306    
307            // for value in bounds, do the lookup...
308            int low = 0;
309            int high = this.lookupTable.size() - 1;
310            while (high - low > 1) {
311                int current = (low + high) / 2;
312                item = (PaintItem) this.lookupTable.get(current);
313                if (value >= item.value) {
314                    low = current;
315                }
316                else {
317                    high = current;
318                }
319            }
320            if (high > low) {
321                item = (PaintItem) this.lookupTable.get(high);
322                if (value < item.value) {
323                    item = (PaintItem) this.lookupTable.get(low);
324                }
325            }
326            return (item != null ? item.paint : this.defaultPaint);
327        }
328    
329    
330        /**
331         * Tests this instance for equality with an arbitrary object.
332         *
333         * @param obj  the object (<code>null</code> permitted).
334         *
335         * @return A boolean.
336         */
337        public boolean equals(Object obj) {
338            if (obj == this) {
339                return true;
340            }
341            if (!(obj instanceof LookupPaintScale)) {
342                return false;
343            }
344            LookupPaintScale that = (LookupPaintScale) obj;
345            if (this.lowerBound != that.lowerBound) {
346                return false;
347            }
348            if (this.upperBound != that.upperBound) {
349                return false;
350            }
351            if (!PaintUtilities.equal(this.defaultPaint, that.defaultPaint)) {
352                return false;
353            }
354            if (!this.lookupTable.equals(that.lookupTable)) {
355                return false;
356            }
357            return true;
358        }
359    
360        /**
361         * Returns a clone of the instance.
362         *
363         * @return A clone.
364         *
365         * @throws CloneNotSupportedException if there is a problem cloning the
366         *     instance.
367         */
368        public Object clone() throws CloneNotSupportedException {
369            LookupPaintScale clone = (LookupPaintScale) super.clone();
370            clone.lookupTable = new java.util.ArrayList(this.lookupTable);
371            return clone;
372        }
373    
374        /**
375         * Provides serialization support.
376         *
377         * @param stream  the output stream.
378         *
379         * @throws IOException  if there is an I/O error.
380         */
381        private void writeObject(ObjectOutputStream stream) throws IOException {
382            stream.defaultWriteObject();
383            SerialUtilities.writePaint(this.defaultPaint, stream);
384        }
385    
386        /**
387         * Provides serialization support.
388         *
389         * @param stream  the input stream.
390         *
391         * @throws IOException  if there is an I/O error.
392         * @throws ClassNotFoundException  if there is a classpath problem.
393         */
394        private void readObject(ObjectInputStream stream)
395                throws IOException, ClassNotFoundException {
396            stream.defaultReadObject();
397            this.defaultPaint = SerialUtilities.readPaint(stream);
398        }
399    
400    }