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 }