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}