    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2007, 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     * LookupPaintScale.java
    029     * ---------------------
    030     * (C) Copyright 2006, 2007, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: LookupPaintScale.java,v 1.1.2.3 2007/03/09 15:59:21 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 05-Jul-2006 : Version 1 (DG);
    040     * 31-Jan-2007 : Fixed serialization support (DG);
    041     * 09-Mar-2007 : Fixed cloning (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        class PaintItem implements Comparable, Serializable {
    073
    074            /** The value. */
    075            Number value;
    076
    077            /** The paint. */
    078            transient Paint paint;
    079
    080            /**
    081             * Creates a new instance.
    082             *
    083             * @param value  the value.
    084             * @param paint  the paint.
    085             */
    086            public PaintItem(Number value, Paint paint) {
    087                this.value = value;
    088                this.paint = paint;
    089            }
    090
    091            /* (non-Javadoc)
    092             * @see java.lang.Comparable#compareTo(java.lang.Object)
    093             */
    094            public int compareTo(Object obj) {
    095                PaintItem that = (PaintItem) obj;
    096                double d1 = this.value.doubleValue();
    097                double d2 = that.value.doubleValue();
    098                if (d1 > d2) {
    099                    return 1;
    100                }
    101                if (d1 < d2) {
    102                    return -1;
    103                }
    104                return 0;
    105            }
    106
    107            /**
    108             * Tests this item for equality with an arbitrary object.
    109             *
    110             * @param obj  the object (<code>null</code> permitted).
    111             *
    112             * @return A boolean.
    113             */
    114            public boolean equals(Object obj) {
    115                if (obj == this) {
    116                    return true;
    117                }
    118                if (!(obj instanceof PaintItem)) {
    119                    return false;
    120                }
    121                PaintItem that = (PaintItem) obj;
    122                if (!this.value.equals(that.value)) {
    123                    return false;
    124                }
    125                if (!PaintUtilities.equal(this.paint, that.paint)) {
    126                    return false;
    127                }
    128                return true;
    129            }
    130
    131            /**
    132             * Provides serialization support.
    133             *
    134             * @param stream  the output stream.
    135             *
    136             * @throws IOException  if there is an I/O error.
    137             */
    138            private void writeObject(ObjectOutputStream stream) throws IOException {
    139                stream.defaultWriteObject();
    140                SerialUtilities.writePaint(this.paint, stream);
    141            }
    142
    143            /**
    144             * Provides serialization support.
    145             *
    146             * @param stream  the input stream.
    147             *
    148             * @throws IOException  if there is an I/O error.
    149             * @throws ClassNotFoundException  if there is a classpath problem.
    150             */
    151            private void readObject(ObjectInputStream stream)
    152                    throws IOException, ClassNotFoundException {
    153                stream.defaultReadObject();
    154                this.paint = SerialUtilities.readPaint(stream);
    155            }
    156
    157        }
    158
    159        /** The lower bound. */
    160        private double lowerBound;
    161
    162        /** The upper bound. */
    163        private double upperBound;
    164
    165        /** The default paint. */
    166        private transient Paint defaultPaint;
    167
    168        /** The lookup table. */
    169        private List lookupTable;
    170
    171        /**
    172         * Creates a new paint scale.
    173         */
    174        public LookupPaintScale() {
    175            this(0.0, 1.0, Color.lightGray);
    176        }
    177
    178        /**
    179         * Creates a new paint scale with the specified default paint.
    180         *
    181         * @param lowerBound  the lower bound.
    182         * @param upperBound  the upper bound.
    183         * @param defaultPaint  the default paint (<code>null</code> not
    184         *     permitted).
    185         */
    186        public LookupPaintScale(double lowerBound, double upperBound,
    187                Paint defaultPaint) {
    188            if (lowerBound >= upperBound) {
    189                throw new IllegalArgumentException(
    190                        "Requires lowerBound < upperBound.");
    191            }
    192            if (defaultPaint == null) {
    193                throw new IllegalArgumentException("Null 'paint' argument.");
    194            }
    195            this.lowerBound = lowerBound;
    196            this.upperBound = upperBound;
    197            this.defaultPaint = defaultPaint;
    198            this.lookupTable = new java.util.ArrayList();
    199        }
    200
    201        /**
    202         * Returns the default paint (never <code>null</code>).
    203         *
    204         * @return The default paint.
    205         */
    206        public Paint getDefaultPaint() {
    207            return this.defaultPaint;
    208        }
    209
    210        /**
    211         * Returns the lower bound.
    212         *
    213         * @return The lower bound.
    214         *
    215         * @see #getUpperBound()
    216         */
    217        public double getLowerBound() {
    218            return this.lowerBound;
    219        }
    220
    221        /**
    222         * Returns the upper bound.
    223         *
    224         * @return The upper bound.
    225         *
    226         * @see #getLowerBound()
    227         */
    228        public double getUpperBound() {
    229            return this.upperBound;
    230        }
    231
    232        /**
    233         * Adds an entry to the lookup table.  Any values from <code>n</code> up
    234         * to but not including the next value in the table take on the specified
    235         * <code>paint</code>.
    236         *
    237         * @param value  the data value.
    238         * @param paint  the paint.
    239         */
    240        public void add(Number value, Paint paint) {
    241            PaintItem item = new PaintItem(value, paint);
    242            int index = Collections.binarySearch(this.lookupTable, item);
    243            if (index >= 0) {
    244                this.lookupTable.set(index, item);
    245            }
    246            else {
    247                this.lookupTable.add(-(index + 1), item);
    248            }
    249        }
    250
    251        /**
    252         * Returns the paint associated with the specified value.
    253         *
    254         * @param value  the value.
    255         *
    256         * @return The paint.
    257         *
    258         * @see #getDefaultPaint()
    259         */
    260        public Paint getPaint(double value) {
    261
    262            // handle value outside bounds...
    263            if (value < this.lowerBound) {
    264                return this.defaultPaint;
    265            }
    266            if (value > this.upperBound) {
    267                return this.defaultPaint;
    268            }
    269
    270            // for value in bounds, do the lookup...
    271            Paint result = this.defaultPaint;
    272            int index = this.lookupTable.size();
    273            boolean done = false;
    274            while (index > 0 && !done) {
    275                PaintItem item = (PaintItem) this.lookupTable.get(--index);
    276                if (value >= item.value.doubleValue()) {
    277                    result = item.paint;
    278                    done = true;
    279                }
    280            }
    281            return result;
    282        }
    283
    284        /**
    285         * Tests this instance for equality with an arbitrary object.
    286         *
    287         * @param obj  the object (<code>null</code> permitted).
    288         *
    289         * @return A boolean.
    290         */
    291        public boolean equals(Object obj) {
    292            if (obj == this) {
    293                return true;
    294            }
    295            if (!(obj instanceof LookupPaintScale)) {
    296                return false;
    297            }
    298            LookupPaintScale that = (LookupPaintScale) obj;
    299            if (this.lowerBound != that.lowerBound) {
    300                return false;
    301            }
    302            if (this.upperBound != that.upperBound) {
    303                return false;
    304            }
    305            if (!PaintUtilities.equal(this.defaultPaint, that.defaultPaint)) {
    306                return false;
    307            }
    308            if (!this.lookupTable.equals(that.lookupTable)) {
    309                return false;
    310            }
    311            return true;
    312        }
    313
    314        /**
    315         * Returns a clone of the instance.
    316         *
    317         * @return A clone.
    318         *
    319         * @throws CloneNotSupportedException if there is a problem cloning the
    320         *     instance.
    321         */
    322        public Object clone() throws CloneNotSupportedException {
    323            LookupPaintScale clone = (LookupPaintScale) super.clone();
    324            clone.lookupTable = new java.util.ArrayList(this.lookupTable);
    325            return clone;
    326        }
    327
    328        /**
    329         * Provides serialization support.
    330         *
    331         * @param stream  the output stream.
    332         *
    333         * @throws IOException  if there is an I/O error.
    334         */
    335        private void writeObject(ObjectOutputStream stream) throws IOException {
    336            stream.defaultWriteObject();
    337            SerialUtilities.writePaint(this.defaultPaint, stream);
    338        }
    339
    340        /**
    341         * Provides serialization support.
    342         *
    343         * @param stream  the input stream.
    344         *
    345         * @throws IOException  if there is an I/O error.
    346         * @throws ClassNotFoundException  if there is a classpath problem.
    347         */
    348        private void readObject(ObjectInputStream stream)
    349                throws IOException, ClassNotFoundException {
    350            stream.defaultReadObject();
    351            this.defaultPaint = SerialUtilities.readPaint(stream);
    352        }
    353
    354    }