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