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