001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2009, 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-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 /* (non-Javadoc) 095 * @see java.lang.Comparable#compareTo(java.lang.Object) 096 */ 097 public int compareTo(Object obj) { 098 PaintItem that = (PaintItem) obj; 099 double d1 = this.value; 100 double d2 = that.value; 101 if (d1 > d2) { 102 return 1; 103 } 104 if (d1 < d2) { 105 return -1; 106 } 107 return 0; 108 } 109 110 /** 111 * Tests this item for equality with an arbitrary object. 112 * 113 * @param obj the object (<code>null</code> permitted). 114 * 115 * @return A boolean. 116 */ 117 public boolean equals(Object obj) { 118 if (obj == this) { 119 return true; 120 } 121 if (!(obj instanceof PaintItem)) { 122 return false; 123 } 124 PaintItem that = (PaintItem) obj; 125 if (this.value != that.value) { 126 return false; 127 } 128 if (!PaintUtilities.equal(this.paint, that.paint)) { 129 return false; 130 } 131 return true; 132 } 133 134 /** 135 * Provides serialization support. 136 * 137 * @param stream the output stream. 138 * 139 * @throws IOException if there is an I/O error. 140 */ 141 private void writeObject(ObjectOutputStream stream) throws IOException { 142 stream.defaultWriteObject(); 143 SerialUtilities.writePaint(this.paint, stream); 144 } 145 146 /** 147 * Provides serialization support. 148 * 149 * @param stream the input stream. 150 * 151 * @throws IOException if there is an I/O error. 152 * @throws ClassNotFoundException if there is a classpath problem. 153 */ 154 private void readObject(ObjectInputStream stream) 155 throws IOException, ClassNotFoundException { 156 stream.defaultReadObject(); 157 this.paint = SerialUtilities.readPaint(stream); 158 } 159 160 } 161 162 /** For serialization. */ 163 static final long serialVersionUID = -5239384246251042006L; 164 165 /** The lower bound. */ 166 private double lowerBound; 167 168 /** The upper bound. */ 169 private double upperBound; 170 171 /** The default paint. */ 172 private transient Paint defaultPaint; 173 174 /** The lookup table. */ 175 private List lookupTable; 176 177 /** 178 * Creates a new paint scale. 179 */ 180 public LookupPaintScale() { 181 this(0.0, 1.0, Color.lightGray); 182 } 183 184 /** 185 * Creates a new paint scale with the specified default paint. 186 * 187 * @param lowerBound the lower bound. 188 * @param upperBound the upper bound. 189 * @param defaultPaint the default paint (<code>null</code> not 190 * permitted). 191 */ 192 public LookupPaintScale(double lowerBound, double upperBound, 193 Paint defaultPaint) { 194 if (lowerBound >= upperBound) { 195 throw new IllegalArgumentException( 196 "Requires lowerBound < upperBound."); 197 } 198 if (defaultPaint == null) { 199 throw new IllegalArgumentException("Null 'paint' argument."); 200 } 201 this.lowerBound = lowerBound; 202 this.upperBound = upperBound; 203 this.defaultPaint = defaultPaint; 204 this.lookupTable = new java.util.ArrayList(); 205 } 206 207 /** 208 * Returns the default paint (never <code>null</code>). 209 * 210 * @return The default paint. 211 */ 212 public Paint getDefaultPaint() { 213 return this.defaultPaint; 214 } 215 216 /** 217 * Returns the lower bound. 218 * 219 * @return The lower bound. 220 * 221 * @see #getUpperBound() 222 */ 223 public double getLowerBound() { 224 return this.lowerBound; 225 } 226 227 /** 228 * Returns the upper bound. 229 * 230 * @return The upper bound. 231 * 232 * @see #getLowerBound() 233 */ 234 public double getUpperBound() { 235 return this.upperBound; 236 } 237 238 /** 239 * Adds an entry to the lookup table. Any values from <code>n</code> up 240 * to but not including the next value in the table take on the specified 241 * <code>paint</code>. 242 * 243 * @param value the data value (<code>null</code> not permitted). 244 * @param paint the paint. 245 * 246 * @deprecated Use {@link #add(double, Paint)}. 247 */ 248 public void add(Number value, Paint paint) { 249 add(value.doubleValue(), paint); 250 } 251 252 /** 253 * Adds an entry to the lookup table. Any values from <code>n</code> up 254 * to but not including the next value in the table take on the specified 255 * <code>paint</code>. 256 * 257 * @param value the data value. 258 * @param paint the paint. 259 * 260 * @since 1.0.6 261 */ 262 public void add(double value, Paint paint) { 263 PaintItem item = new PaintItem(value, paint); 264 int index = Collections.binarySearch(this.lookupTable, item); 265 if (index >= 0) { 266 this.lookupTable.set(index, item); 267 } 268 else { 269 this.lookupTable.add(-(index + 1), item); 270 } 271 } 272 273 /** 274 * Returns the paint associated with the specified value. 275 * 276 * @param value the value. 277 * 278 * @return The paint. 279 * 280 * @see #getDefaultPaint() 281 */ 282 public Paint getPaint(double value) { 283 284 // handle value outside bounds... 285 if (value < this.lowerBound) { 286 return this.defaultPaint; 287 } 288 if (value > this.upperBound) { 289 return this.defaultPaint; 290 } 291 292 int count = this.lookupTable.size(); 293 if (count == 0) { 294 return this.defaultPaint; 295 } 296 297 // handle special case where value is less that item zero 298 PaintItem item = (PaintItem) this.lookupTable.get(0); 299 if (value < item.value) { 300 return this.defaultPaint; 301 } 302 303 // for value in bounds, do the lookup... 304 int low = 0; 305 int high = this.lookupTable.size() - 1; 306 while (high - low > 1) { 307 int current = (low + high) / 2; 308 item = (PaintItem) this.lookupTable.get(current); 309 if (value >= item.value) { 310 low = current; 311 } 312 else { 313 high = current; 314 } 315 } 316 if (high > low) { 317 item = (PaintItem) this.lookupTable.get(high); 318 if (value < item.value) { 319 item = (PaintItem) this.lookupTable.get(low); 320 } 321 } 322 return (item != null ? item.paint : this.defaultPaint); 323 } 324 325 326 /** 327 * Tests this instance for equality with an arbitrary object. 328 * 329 * @param obj the object (<code>null</code> permitted). 330 * 331 * @return A boolean. 332 */ 333 public boolean equals(Object obj) { 334 if (obj == this) { 335 return true; 336 } 337 if (!(obj instanceof LookupPaintScale)) { 338 return false; 339 } 340 LookupPaintScale that = (LookupPaintScale) obj; 341 if (this.lowerBound != that.lowerBound) { 342 return false; 343 } 344 if (this.upperBound != that.upperBound) { 345 return false; 346 } 347 if (!PaintUtilities.equal(this.defaultPaint, that.defaultPaint)) { 348 return false; 349 } 350 if (!this.lookupTable.equals(that.lookupTable)) { 351 return false; 352 } 353 return true; 354 } 355 356 /** 357 * Returns a clone of the instance. 358 * 359 * @return A clone. 360 * 361 * @throws CloneNotSupportedException if there is a problem cloning the 362 * instance. 363 */ 364 public Object clone() throws CloneNotSupportedException { 365 LookupPaintScale clone = (LookupPaintScale) super.clone(); 366 clone.lookupTable = new java.util.ArrayList(this.lookupTable); 367 return clone; 368 } 369 370 /** 371 * Provides serialization support. 372 * 373 * @param stream the output stream. 374 * 375 * @throws IOException if there is an I/O error. 376 */ 377 private void writeObject(ObjectOutputStream stream) throws IOException { 378 stream.defaultWriteObject(); 379 SerialUtilities.writePaint(this.defaultPaint, stream); 380 } 381 382 /** 383 * Provides serialization support. 384 * 385 * @param stream the input stream. 386 * 387 * @throws IOException if there is an I/O error. 388 * @throws ClassNotFoundException if there is a classpath problem. 389 */ 390 private void readObject(ObjectInputStream stream) 391 throws IOException, ClassNotFoundException { 392 stream.defaultReadObject(); 393 this.defaultPaint = SerialUtilities.readPaint(stream); 394 } 395 396 }