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 * CategoryLineAnnotation.java 029 * --------------------------- 030 * (C) Copyright 2005-2007, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes: 036 * -------- 037 * 29-Jul-2005 : Version 1, based on CategoryTextAnnotation (DG); 038 * ------------- JFREECHART 1.0.x --------------------------------------------- 039 * 06-Mar-2007 : Reimplemented hashCode() (DG); 040 * 041 */ 042 043 package org.jfree.chart.annotations; 044 045 import java.awt.BasicStroke; 046 import java.awt.Color; 047 import java.awt.Graphics2D; 048 import java.awt.Paint; 049 import java.awt.Stroke; 050 import java.awt.geom.Rectangle2D; 051 import java.io.IOException; 052 import java.io.ObjectInputStream; 053 import java.io.ObjectOutputStream; 054 import java.io.Serializable; 055 056 import org.jfree.chart.HashUtilities; 057 import org.jfree.chart.axis.CategoryAnchor; 058 import org.jfree.chart.axis.CategoryAxis; 059 import org.jfree.chart.axis.ValueAxis; 060 import org.jfree.chart.plot.CategoryPlot; 061 import org.jfree.chart.plot.Plot; 062 import org.jfree.chart.plot.PlotOrientation; 063 import org.jfree.data.category.CategoryDataset; 064 import org.jfree.io.SerialUtilities; 065 import org.jfree.ui.RectangleEdge; 066 import org.jfree.util.ObjectUtilities; 067 import org.jfree.util.PaintUtilities; 068 069 /** 070 * A line annotation that can be placed on a {@link CategoryPlot}. 071 */ 072 public class CategoryLineAnnotation implements CategoryAnnotation, 073 Cloneable, Serializable { 074 075 /** For serialization. */ 076 static final long serialVersionUID = 3477740483341587984L; 077 078 /** The category for the start of the line. */ 079 private Comparable category1; 080 081 /** The value for the start of the line. */ 082 private double value1; 083 084 /** The category for the end of the line. */ 085 private Comparable category2; 086 087 /** The value for the end of the line. */ 088 private double value2; 089 090 /** The line color. */ 091 private transient Paint paint = Color.black; 092 093 /** The line stroke. */ 094 private transient Stroke stroke = new BasicStroke(1.0f); 095 096 /** 097 * Creates a new annotation that draws a line between (category1, value1) 098 * and (category2, value2). 099 * 100 * @param category1 the category (<code>null</code> not permitted). 101 * @param value1 the value. 102 * @param category2 the category (<code>null</code> not permitted). 103 * @param value2 the value. 104 * @param paint the line color (<code>null</code> not permitted). 105 * @param stroke the line stroke (<code>null</code> not permitted). 106 */ 107 public CategoryLineAnnotation(Comparable category1, double value1, 108 Comparable category2, double value2, 109 Paint paint, Stroke stroke) { 110 if (category1 == null) { 111 throw new IllegalArgumentException("Null 'category1' argument."); 112 } 113 if (category2 == null) { 114 throw new IllegalArgumentException("Null 'category2' argument."); 115 } 116 if (paint == null) { 117 throw new IllegalArgumentException("Null 'paint' argument."); 118 } 119 if (stroke == null) { 120 throw new IllegalArgumentException("Null 'stroke' argument."); 121 } 122 this.category1 = category1; 123 this.value1 = value1; 124 this.category2 = category2; 125 this.value2 = value2; 126 this.paint = paint; 127 this.stroke = stroke; 128 } 129 130 /** 131 * Returns the category for the start of the line. 132 * 133 * @return The category for the start of the line (never <code>null</code>). 134 * 135 * @see #setCategory1(Comparable) 136 */ 137 public Comparable getCategory1() { 138 return this.category1; 139 } 140 141 /** 142 * Sets the category for the start of the line. 143 * 144 * @param category the category (<code>null</code> not permitted). 145 * 146 * @see #getCategory1() 147 */ 148 public void setCategory1(Comparable category) { 149 if (category == null) { 150 throw new IllegalArgumentException("Null 'category' argument."); 151 } 152 this.category1 = category; 153 } 154 155 /** 156 * Returns the y-value for the start of the line. 157 * 158 * @return The y-value for the start of the line. 159 * 160 * @see #setValue1(double) 161 */ 162 public double getValue1() { 163 return this.value1; 164 } 165 166 /** 167 * Sets the y-value for the start of the line. 168 * 169 * @param value the value. 170 * 171 * @see #getValue1() 172 */ 173 public void setValue1(double value) { 174 this.value1 = value; 175 } 176 177 /** 178 * Returns the category for the end of the line. 179 * 180 * @return The category for the end of the line (never <code>null</code>). 181 * 182 * @see #setCategory2(Comparable) 183 */ 184 public Comparable getCategory2() { 185 return this.category2; 186 } 187 188 /** 189 * Sets the category for the end of the line. 190 * 191 * @param category the category (<code>null</code> not permitted). 192 * 193 * @see #getCategory2() 194 */ 195 public void setCategory2(Comparable category) { 196 if (category == null) { 197 throw new IllegalArgumentException("Null 'category' argument."); 198 } 199 this.category2 = category; 200 } 201 202 /** 203 * Returns the y-value for the end of the line. 204 * 205 * @return The y-value for the end of the line. 206 * 207 * @see #setValue2(double) 208 */ 209 public double getValue2() { 210 return this.value2; 211 } 212 213 /** 214 * Sets the y-value for the end of the line. 215 * 216 * @param value the value. 217 * 218 * @see #getValue2() 219 */ 220 public void setValue2(double value) { 221 this.value2 = value; 222 } 223 224 /** 225 * Returns the paint used to draw the connecting line. 226 * 227 * @return The paint (never <code>null</code>). 228 * 229 * @see #setPaint(Paint) 230 */ 231 public Paint getPaint() { 232 return this.paint; 233 } 234 235 /** 236 * Sets the paint used to draw the connecting line. 237 * 238 * @param paint the paint (<code>null</code> not permitted). 239 * 240 * @see #getPaint() 241 */ 242 public void setPaint(Paint paint) { 243 if (paint == null) { 244 throw new IllegalArgumentException("Null 'paint' argument."); 245 } 246 this.paint = paint; 247 } 248 249 /** 250 * Returns the stroke used to draw the connecting line. 251 * 252 * @return The stroke (never <code>null</code>). 253 * 254 * @see #setStroke(Stroke) 255 */ 256 public Stroke getStroke() { 257 return this.stroke; 258 } 259 260 /** 261 * Sets the stroke used to draw the connecting line. 262 * 263 * @param stroke the stroke (<code>null</code> not permitted). 264 * 265 * @see #getStroke() 266 */ 267 public void setStroke(Stroke stroke) { 268 if (stroke == null) { 269 throw new IllegalArgumentException("Null 'stroke' argument."); 270 } 271 this.stroke = stroke; 272 } 273 274 /** 275 * Draws the annotation. 276 * 277 * @param g2 the graphics device. 278 * @param plot the plot. 279 * @param dataArea the data area. 280 * @param domainAxis the domain axis. 281 * @param rangeAxis the range axis. 282 */ 283 public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, 284 CategoryAxis domainAxis, ValueAxis rangeAxis) { 285 286 CategoryDataset dataset = plot.getDataset(); 287 int catIndex1 = dataset.getColumnIndex(this.category1); 288 int catIndex2 = dataset.getColumnIndex(this.category2); 289 int catCount = dataset.getColumnCount(); 290 291 double lineX1 = 0.0f; 292 double lineY1 = 0.0f; 293 double lineX2 = 0.0f; 294 double lineY2 = 0.0f; 295 PlotOrientation orientation = plot.getOrientation(); 296 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 297 plot.getDomainAxisLocation(), orientation); 298 RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( 299 plot.getRangeAxisLocation(), orientation); 300 301 if (orientation == PlotOrientation.HORIZONTAL) { 302 lineY1 = domainAxis.getCategoryJava2DCoordinate( 303 CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea, 304 domainEdge); 305 lineX1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge); 306 lineY2 = domainAxis.getCategoryJava2DCoordinate( 307 CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea, 308 domainEdge); 309 lineX2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge); 310 } 311 else if (orientation == PlotOrientation.VERTICAL) { 312 lineX1 = domainAxis.getCategoryJava2DCoordinate( 313 CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea, 314 domainEdge); 315 lineY1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge); 316 lineX2 = domainAxis.getCategoryJava2DCoordinate( 317 CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea, 318 domainEdge); 319 lineY2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge); 320 } 321 g2.setPaint(this.paint); 322 g2.setStroke(this.stroke); 323 g2.drawLine((int) lineX1, (int) lineY1, (int) lineX2, (int) lineY2); 324 } 325 326 /** 327 * Tests this object for equality with another. 328 * 329 * @param obj the object (<code>null</code> permitted). 330 * 331 * @return <code>true</code> or <code>false</code>. 332 */ 333 public boolean equals(Object obj) { 334 if (obj == this) { 335 return true; 336 } 337 if (!(obj instanceof CategoryLineAnnotation)) { 338 return false; 339 } 340 CategoryLineAnnotation that = (CategoryLineAnnotation) obj; 341 if (!this.category1.equals(that.getCategory1())) { 342 return false; 343 } 344 if (this.value1 != that.getValue1()) { 345 return false; 346 } 347 if (!this.category2.equals(that.getCategory2())) { 348 return false; 349 } 350 if (this.value2 != that.getValue2()) { 351 return false; 352 } 353 if (!PaintUtilities.equal(this.paint, that.paint)) { 354 return false; 355 } 356 if (!ObjectUtilities.equal(this.stroke, that.stroke)) { 357 return false; 358 } 359 return true; 360 } 361 362 /** 363 * Returns a hash code for this instance. 364 * 365 * @return A hash code. 366 */ 367 public int hashCode() { 368 int result = 193; 369 result = 37 * result + this.category1.hashCode(); 370 long temp = Double.doubleToLongBits(this.value1); 371 result = 37 * result + (int) (temp ^ (temp >>> 32)); 372 result = 37 * result + this.category2.hashCode(); 373 temp = Double.doubleToLongBits(this.value2); 374 result = 37 * result + (int) (temp ^ (temp >>> 32)); 375 result = 37 * result + HashUtilities.hashCodeForPaint(this.paint); 376 result = 37 * result + this.stroke.hashCode(); 377 return result; 378 } 379 380 /** 381 * Returns a clone of the annotation. 382 * 383 * @return A clone. 384 * 385 * @throws CloneNotSupportedException this class will not throw this 386 * exception, but subclasses (if any) might. 387 */ 388 public Object clone() throws CloneNotSupportedException { 389 return super.clone(); 390 } 391 392 /** 393 * Provides serialization support. 394 * 395 * @param stream the output stream. 396 * 397 * @throws IOException if there is an I/O error. 398 */ 399 private void writeObject(ObjectOutputStream stream) throws IOException { 400 stream.defaultWriteObject(); 401 SerialUtilities.writePaint(this.paint, stream); 402 SerialUtilities.writeStroke(this.stroke, stream); 403 } 404 405 /** 406 * Provides serialization support. 407 * 408 * @param stream the input stream. 409 * 410 * @throws IOException if there is an I/O error. 411 * @throws ClassNotFoundException if there is a classpath problem. 412 */ 413 private void readObject(ObjectInputStream stream) 414 throws IOException, ClassNotFoundException { 415 stream.defaultReadObject(); 416 this.paint = SerialUtilities.readPaint(stream); 417 this.stroke = SerialUtilities.readStroke(stream); 418 } 419 420 }