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: * XYTextAnnotation.java
029: * ---------------------
030: * (C) Copyright 2002-2007, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: XYTextAnnotation.java,v 1.5.2.4 2007/03/06 16:12:19 mungady Exp $
036: *
037: * Changes:
038: * --------
039: * 28-Aug-2002 : Version 1 (DG);
040: * 07-Nov-2002 : Fixed errors reported by Checkstyle (DG);
041: * 13-Jan-2003 : Reviewed Javadocs (DG);
042: * 26-Mar-2003 : Implemented Serializable (DG);
043: * 02-Jul-2003 : Added new text alignment and rotation options (DG);
044: * 19-Aug-2003 : Implemented Cloneable (DG);
045: * 17-Jan-2003 : Added fix for bug 878706, where the annotation is placed
046: * incorrectly for a plot with horizontal orientation (thanks to
047: * Ed Yu for the fix) (DG);
048: * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
049: * ------------- JFREECHART 1.0.x ---------------------------------------------
050: * 26-Jan-2006 : Fixed equals() method (bug 1415480) (DG);
051: * 06-Mar-2007 : Added argument checks, re-implemented hashCode() method (DG);
052: *
053: */
054:
055: package org.jfree.chart.annotations;
056:
057: import java.awt.Color;
058: import java.awt.Font;
059: import java.awt.Graphics2D;
060: import java.awt.Paint;
061: import java.awt.Shape;
062: import java.awt.geom.Rectangle2D;
063: import java.io.IOException;
064: import java.io.ObjectInputStream;
065: import java.io.ObjectOutputStream;
066: import java.io.Serializable;
067:
068: import org.jfree.chart.HashUtilities;
069: import org.jfree.chart.axis.ValueAxis;
070: import org.jfree.chart.plot.Plot;
071: import org.jfree.chart.plot.PlotOrientation;
072: import org.jfree.chart.plot.PlotRenderingInfo;
073: import org.jfree.chart.plot.XYPlot;
074: import org.jfree.io.SerialUtilities;
075: import org.jfree.text.TextUtilities;
076: import org.jfree.ui.RectangleEdge;
077: import org.jfree.ui.TextAnchor;
078: import org.jfree.util.PaintUtilities;
079: import org.jfree.util.PublicCloneable;
080:
081: /**
082: * A text annotation that can be placed at a particular (x, y) location on an
083: * {@link XYPlot}.
084: */
085: public class XYTextAnnotation extends AbstractXYAnnotation implements
086: Cloneable, PublicCloneable, Serializable {
087:
088: /** For serialization. */
089: private static final long serialVersionUID = -2946063342782506328L;
090:
091: /** The default font. */
092: public static final Font DEFAULT_FONT = new Font("SansSerif",
093: Font.PLAIN, 10);
094:
095: /** The default paint. */
096: public static final Paint DEFAULT_PAINT = Color.black;
097:
098: /** The default text anchor. */
099: public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER;
100:
101: /** The default rotation anchor. */
102: public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER;
103:
104: /** The default rotation angle. */
105: public static final double DEFAULT_ROTATION_ANGLE = 0.0;
106:
107: /** The text. */
108: private String text;
109:
110: /** The font. */
111: private Font font;
112:
113: /** The paint. */
114: private transient Paint paint;
115:
116: /** The x-coordinate. */
117: private double x;
118:
119: /** The y-coordinate. */
120: private double y;
121:
122: /** The text anchor (to be aligned with (x, y)). */
123: private TextAnchor textAnchor;
124:
125: /** The rotation anchor. */
126: private TextAnchor rotationAnchor;
127:
128: /** The rotation angle. */
129: private double rotationAngle;
130:
131: /**
132: * Creates a new annotation to be displayed at the given coordinates. The
133: * coordinates are specified in data space (they will be converted to
134: * Java2D space for display).
135: *
136: * @param text the text (<code>null</code> not permitted).
137: * @param x the x-coordinate (in data space).
138: * @param y the y-coordinate (in data space).
139: */
140: public XYTextAnnotation(String text, double x, double y) {
141: if (text == null) {
142: throw new IllegalArgumentException("Null 'text' argument.");
143: }
144: this .text = text;
145: this .font = DEFAULT_FONT;
146: this .paint = DEFAULT_PAINT;
147: this .x = x;
148: this .y = y;
149: this .textAnchor = DEFAULT_TEXT_ANCHOR;
150: this .rotationAnchor = DEFAULT_ROTATION_ANCHOR;
151: this .rotationAngle = DEFAULT_ROTATION_ANGLE;
152: }
153:
154: /**
155: * Returns the text for the annotation.
156: *
157: * @return The text (never <code>null</code>).
158: *
159: * @see #setText(String)
160: */
161: public String getText() {
162: return this .text;
163: }
164:
165: /**
166: * Sets the text for the annotation.
167: *
168: * @param text the text (<code>null</code> not permitted).
169: *
170: * @see #getText()
171: */
172: public void setText(String text) {
173: if (text == null) {
174: throw new IllegalArgumentException("Null 'text' argument.");
175: }
176: this .text = text;
177: }
178:
179: /**
180: * Returns the font for the annotation.
181: *
182: * @return The font (never <code>null</code>).
183: *
184: * @see #setFont(Font)
185: */
186: public Font getFont() {
187: return this .font;
188: }
189:
190: /**
191: * Sets the font for the annotation.
192: *
193: * @param font the font (<code>null</code> not permitted).
194: *
195: * @see #getFont()
196: */
197: public void setFont(Font font) {
198: if (font == null) {
199: throw new IllegalArgumentException("Null 'font' argument.");
200: }
201: this .font = font;
202: }
203:
204: /**
205: * Returns the paint for the annotation.
206: *
207: * @return The paint (never <code>null</code>).
208: *
209: * @see #setPaint(Paint)
210: */
211: public Paint getPaint() {
212: return this .paint;
213: }
214:
215: /**
216: * Sets the paint for the annotation.
217: *
218: * @param paint the paint (<code>null</code> not permitted).
219: *
220: * @see #getPaint()
221: */
222: public void setPaint(Paint paint) {
223: if (paint == null) {
224: throw new IllegalArgumentException("Null 'paint' argument.");
225: }
226: this .paint = paint;
227: }
228:
229: /**
230: * Returns the text anchor.
231: *
232: * @return The text anchor (never <code>null</code>).
233: *
234: * @see #setTextAnchor(TextAnchor)
235: */
236: public TextAnchor getTextAnchor() {
237: return this .textAnchor;
238: }
239:
240: /**
241: * Sets the text anchor (the point on the text bounding rectangle that is
242: * aligned to the (x, y) coordinate of the annotation).
243: *
244: * @param anchor the anchor point (<code>null</code> not permitted).
245: *
246: * @see #getTextAnchor()
247: */
248: public void setTextAnchor(TextAnchor anchor) {
249: if (anchor == null) {
250: throw new IllegalArgumentException(
251: "Null 'anchor' argument.");
252: }
253: this .textAnchor = anchor;
254: }
255:
256: /**
257: * Returns the rotation anchor.
258: *
259: * @return The rotation anchor point (never <code>null</code>).
260: *
261: * @see #setRotationAnchor(TextAnchor)
262: */
263: public TextAnchor getRotationAnchor() {
264: return this .rotationAnchor;
265: }
266:
267: /**
268: * Sets the rotation anchor point.
269: *
270: * @param anchor the anchor (<code>null</code> not permitted).
271: *
272: * @see #getRotationAnchor()
273: */
274: public void setRotationAnchor(TextAnchor anchor) {
275: if (anchor == null) {
276: throw new IllegalArgumentException(
277: "Null 'anchor' argument.");
278: }
279: this .rotationAnchor = anchor;
280: }
281:
282: /**
283: * Returns the rotation angle.
284: *
285: * @return The rotation angle.
286: *
287: * @see #setRotationAngle(double)
288: */
289: public double getRotationAngle() {
290: return this .rotationAngle;
291: }
292:
293: /**
294: * Sets the rotation angle. The angle is measured clockwise in radians.
295: *
296: * @param angle the angle (in radians).
297: *
298: * @see #getRotationAngle()
299: */
300: public void setRotationAngle(double angle) {
301: this .rotationAngle = angle;
302: }
303:
304: /**
305: * Returns the x coordinate for the text anchor point (measured against the
306: * domain axis).
307: *
308: * @return The x coordinate (in data space).
309: *
310: * @see #setX(double)
311: */
312: public double getX() {
313: return this .x;
314: }
315:
316: /**
317: * Sets the x coordinate for the text anchor point (measured against the
318: * domain axis).
319: *
320: * @param x the x coordinate (in data space).
321: *
322: * @see #getX()
323: */
324: public void setX(double x) {
325: this .x = x;
326: }
327:
328: /**
329: * Returns the y coordinate for the text anchor point (measured against the
330: * range axis).
331: *
332: * @return The y coordinate (in data space).
333: *
334: * @see #setY(double)
335: */
336: public double getY() {
337: return this .y;
338: }
339:
340: /**
341: * Sets the y coordinate for the text anchor point (measured against the
342: * range axis).
343: *
344: * @param y the y coordinate.
345: *
346: * @see #getY()
347: */
348: public void setY(double y) {
349: this .y = y;
350: }
351:
352: /**
353: * Draws the annotation.
354: *
355: * @param g2 the graphics device.
356: * @param plot the plot.
357: * @param dataArea the data area.
358: * @param domainAxis the domain axis.
359: * @param rangeAxis the range axis.
360: * @param rendererIndex the renderer index.
361: * @param info an optional info object that will be populated with
362: * entity information.
363: */
364: public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
365: ValueAxis domainAxis, ValueAxis rangeAxis,
366: int rendererIndex, PlotRenderingInfo info) {
367:
368: PlotOrientation orientation = plot.getOrientation();
369: RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(plot
370: .getDomainAxisLocation(), orientation);
371: RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(plot
372: .getRangeAxisLocation(), orientation);
373:
374: float anchorX = (float) domainAxis.valueToJava2D(this .x,
375: dataArea, domainEdge);
376: float anchorY = (float) rangeAxis.valueToJava2D(this .y,
377: dataArea, rangeEdge);
378:
379: if (orientation == PlotOrientation.HORIZONTAL) {
380: float tempAnchor = anchorX;
381: anchorX = anchorY;
382: anchorY = tempAnchor;
383: }
384:
385: g2.setFont(getFont());
386: g2.setPaint(getPaint());
387: TextUtilities.drawRotatedString(getText(), g2, anchorX,
388: anchorY, getTextAnchor(), getRotationAngle(),
389: getRotationAnchor());
390: Shape hotspot = TextUtilities.calculateRotatedStringBounds(
391: getText(), g2, anchorX, anchorY, getTextAnchor(),
392: getRotationAngle(), getRotationAnchor());
393:
394: String toolTip = getToolTipText();
395: String url = getURL();
396: if (toolTip != null || url != null) {
397: addEntity(info, hotspot, rendererIndex, toolTip, url);
398: }
399:
400: }
401:
402: /**
403: * Tests this annotation for equality with an arbitrary object.
404: *
405: * @param obj the object (<code>null</code> permitted).
406: *
407: * @return A boolean.
408: */
409: public boolean equals(Object obj) {
410: if (obj == this ) {
411: return true;
412: }
413: if (!(obj instanceof XYTextAnnotation)) {
414: return false;
415: }
416: if (!super .equals(obj)) {
417: return false;
418: }
419: XYTextAnnotation that = (XYTextAnnotation) obj;
420: if (!this .text.equals(that.text)) {
421: return false;
422: }
423: if (this .x != that.x) {
424: return false;
425: }
426: if (this .y != that.y) {
427: return false;
428: }
429: if (!this .font.equals(that.font)) {
430: return false;
431: }
432: if (!PaintUtilities.equal(this .paint, that.paint)) {
433: return false;
434: }
435: if (!this .rotationAnchor.equals(that.rotationAnchor)) {
436: return false;
437: }
438: if (this .rotationAngle != that.rotationAngle) {
439: return false;
440: }
441: if (!this .textAnchor.equals(that.textAnchor)) {
442: return false;
443: }
444: return true;
445: }
446:
447: /**
448: * Returns a hash code for the object.
449: *
450: * @return A hash code.
451: */
452: public int hashCode() {
453: int result = 193;
454: result = 37 * this .text.hashCode();
455: result = 37 * this .font.hashCode();
456: result = 37 * result
457: + HashUtilities.hashCodeForPaint(this .paint);
458: long temp = Double.doubleToLongBits(this .x);
459: result = 37 * result + (int) (temp ^ (temp >>> 32));
460: temp = Double.doubleToLongBits(this .y);
461: result = 37 * result + (int) (temp ^ (temp >>> 32));
462: result = 37 * result + this .textAnchor.hashCode();
463: result = 37 * result + this .rotationAnchor.hashCode();
464: temp = Double.doubleToLongBits(this .rotationAngle);
465: result = 37 * result + (int) (temp ^ (temp >>> 32));
466: return result;
467: }
468:
469: /**
470: * Returns a clone of the annotation.
471: *
472: * @return A clone.
473: *
474: * @throws CloneNotSupportedException if the annotation can't be cloned.
475: */
476: public Object clone() throws CloneNotSupportedException {
477: return super .clone();
478: }
479:
480: /**
481: * Provides serialization support.
482: *
483: * @param stream the output stream.
484: *
485: * @throws IOException if there is an I/O error.
486: */
487: private void writeObject(ObjectOutputStream stream)
488: throws IOException {
489: stream.defaultWriteObject();
490: SerialUtilities.writePaint(this .paint, stream);
491: }
492:
493: /**
494: * Provides serialization support.
495: *
496: * @param stream the input stream.
497: *
498: * @throws IOException if there is an I/O error.
499: * @throws ClassNotFoundException if there is a classpath problem.
500: */
501: private void readObject(ObjectInputStream stream)
502: throws IOException, ClassNotFoundException {
503: stream.defaultReadObject();
504: this.paint = SerialUtilities.readPaint(stream);
505: }
506:
507: }
|