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