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 }