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