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