    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
    045package org.jfree.chart.renderer;
    046
    047import java.awt.Color;
    048import java.awt.Paint;
    049import java.io.IOException;
    050import java.io.ObjectInputStream;
    051import java.io.ObjectOutputStream;
    052import java.io.Serializable;
    053import java.util.Collections;
    054import java.util.List;
    055
    056import org.jfree.io.SerialUtilities;
    057import org.jfree.util.PaintUtilities;
    058import 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 */
    066public 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}