Source for org.jfree.chart.annotations.XYTextAnnotation

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------------------
  28:  * XYTextAnnotation.java
  29:  * ---------------------
  30:  * (C) Copyright 2002-2006, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: XYTextAnnotation.java,v 1.5.2.2 2006/01/26 15:30:24 mungady Exp $
  36:  *
  37:  * Changes:
  38:  * --------
  39:  * 28-Aug-2002 : Version 1 (DG);
  40:  * 07-Nov-2002 : Fixed errors reported by Checkstyle (DG);
  41:  * 13-Jan-2003 : Reviewed Javadocs (DG);
  42:  * 26-Mar-2003 : Implemented Serializable (DG);
  43:  * 02-Jul-2003 : Added new text alignment and rotation options (DG);
  44:  * 19-Aug-2003 : Implemented Cloneable (DG);
  45:  * 17-Jan-2003 : Added fix for bug 878706, where the annotation is placed 
  46:  *               incorrectly for a plot with horizontal orientation (thanks to
  47:  *               Ed Yu for the fix) (DG);
  48:  * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
  49:  * ------------- JFREECHART 1.0.0 ---------------------------------------------
  50:  * 26-Jan-2006 : Fixed equals() method (bug 1415480) (DG);
  51:  *
  52:  */
  53: 
  54: package org.jfree.chart.annotations;
  55: 
  56: import java.awt.Color;
  57: import java.awt.Font;
  58: import java.awt.Graphics2D;
  59: import java.awt.Paint;
  60: import java.awt.Shape;
  61: import java.awt.geom.Rectangle2D;
  62: import java.io.IOException;
  63: import java.io.ObjectInputStream;
  64: import java.io.ObjectOutputStream;
  65: import java.io.Serializable;
  66: 
  67: import org.jfree.chart.axis.ValueAxis;
  68: import org.jfree.chart.plot.Plot;
  69: import org.jfree.chart.plot.PlotOrientation;
  70: import org.jfree.chart.plot.PlotRenderingInfo;
  71: import org.jfree.chart.plot.XYPlot;
  72: import org.jfree.io.SerialUtilities;
  73: import org.jfree.text.TextUtilities;
  74: import org.jfree.ui.RectangleEdge;
  75: import org.jfree.ui.TextAnchor;
  76: import org.jfree.util.PaintUtilities;
  77: import org.jfree.util.PublicCloneable;
  78: 
  79: /**
  80:  * A text annotation that can be placed at a particular (x, y) location on an 
  81:  * {@link XYPlot}.
  82:  */
  83: public class XYTextAnnotation extends AbstractXYAnnotation
  84:                               implements Cloneable, PublicCloneable, 
  85:                                          Serializable {
  86: 
  87:     /** For serialization. */
  88:     private static final long serialVersionUID = -2946063342782506328L;
  89:     
  90:     /** The default font. */
  91:     public static final Font DEFAULT_FONT 
  92:         = new Font("SansSerif", Font.PLAIN, 10);
  93: 
  94:     /** The default paint. */
  95:     public static final Paint DEFAULT_PAINT = Color.black;
  96:     
  97:     /** The default text anchor. */
  98:     public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER;
  99: 
 100:     /** The default rotation anchor. */    
 101:     public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER;
 102:     
 103:     /** The default rotation angle. */
 104:     public static final double DEFAULT_ROTATION_ANGLE = 0.0;
 105: 
 106:     /** The text. */
 107:     private String text;
 108: 
 109:     /** The font. */
 110:     private Font font;
 111: 
 112:     /** The paint. */
 113:     private transient Paint paint;
 114:     
 115:     /** The x-coordinate. */
 116:     private double x;
 117: 
 118:     /** The y-coordinate. */
 119:     private double y;
 120: 
 121:     /** The text anchor (to be aligned with (x, y)). */
 122:     private TextAnchor textAnchor;
 123:     
 124:     /** The rotation anchor. */
 125:     private TextAnchor rotationAnchor;
 126:     
 127:     /** The rotation angle. */
 128:     private double rotationAngle;
 129:     
 130:     /**
 131:      * Creates a new annotation to be displayed at the given coordinates.  The
 132:      * coordinates are specified in data space (they will be converted to 
 133:      * Java2D space for display).
 134:      *
 135:      * @param text  the text (<code>null</code> not permitted).
 136:      * @param x  the x-coordinate (in data space).
 137:      * @param y  the y-coordinate (in data space).
 138:      */
 139:     public XYTextAnnotation(String text, double x, double y) {
 140:         if (text == null) {
 141:             throw new IllegalArgumentException("Null 'text' argument.");
 142:         }
 143:         this.text = text;
 144:         this.font = DEFAULT_FONT;
 145:         this.paint = DEFAULT_PAINT;
 146:         this.x = x;
 147:         this.y = y;
 148:         this.textAnchor = DEFAULT_TEXT_ANCHOR;
 149:         this.rotationAnchor = DEFAULT_ROTATION_ANCHOR;
 150:         this.rotationAngle = DEFAULT_ROTATION_ANGLE;
 151:     }
 152:     
 153:     /**
 154:      * Returns the text for the annotation.
 155:      *
 156:      * @return The text (never <code>null</code>).
 157:      */
 158:     public String getText() {
 159:         return this.text;
 160:     }
 161: 
 162:     /**
 163:      * Sets the text for the annotation.
 164:      * 
 165:      * @param text  the text (<code>null</code> not permitted).
 166:      */
 167:     public void setText(String text) {
 168:         this.text = text;
 169:     }
 170:     
 171:     /**
 172:      * Returns the font for the annotation.
 173:      *
 174:      * @return The font.
 175:      */
 176:     public Font getFont() {
 177:         return this.font;
 178:     }
 179: 
 180:     /**
 181:      * Sets the font for the annotation.
 182:      * 
 183:      * @param font  the font.
 184:      */
 185:     public void setFont(Font font) {
 186:         this.font = font;
 187:     }
 188:     
 189:     /**
 190:      * Returns the paint for the annotation.
 191:      *
 192:      * @return The paint.
 193:      */
 194:     public Paint getPaint() {
 195:         return this.paint;
 196:     }
 197:     
 198:     /**
 199:      * Sets the paint for the annotation.
 200:      * 
 201:      * @param paint  the paint.
 202:      */
 203:     public void setPaint(Paint paint) {
 204:         this.paint = paint;
 205:     }
 206: 
 207:     /**
 208:      * Returns the text anchor.
 209:      * 
 210:      * @return The text anchor.
 211:      */
 212:     public TextAnchor getTextAnchor() {
 213:         return this.textAnchor;
 214:     }
 215:     
 216:     /**
 217:      * Sets the text anchor (the point on the text bounding rectangle that is 
 218:      * aligned to the (x, y) coordinate of the annotation).
 219:      * 
 220:      * @param anchor  the anchor point.
 221:      */
 222:     public void setTextAnchor(TextAnchor anchor) {
 223:         this.textAnchor = anchor;
 224:     }
 225:     
 226:     /**
 227:      * Returns the rotation anchor.
 228:      * 
 229:      * @return The rotation anchor point.
 230:      */
 231:     public TextAnchor getRotationAnchor() {
 232:         return this.rotationAnchor;
 233:     }
 234:     
 235:     /**
 236:      * Sets the rotation anchor point.
 237:      * 
 238:      * @param anchor  the anchor.
 239:      */
 240:     public void setRotationAnchor(TextAnchor anchor) {
 241:         this.rotationAnchor = anchor;    
 242:     }
 243:     
 244:     /**
 245:      * Returns the rotation angle.
 246:      * 
 247:      * @return The rotation angle.
 248:      */
 249:     public double getRotationAngle() {
 250:         return this.rotationAngle; 
 251:     }
 252:     
 253:     /**
 254:      * Sets the rotation angle.
 255:      * <p>
 256:      * The angle is measured clockwise in radians.
 257:      * 
 258:      * @param angle  the angle (in radians).
 259:      */
 260:     public void setRotationAngle(double angle) {
 261:         this.rotationAngle = angle;    
 262:     }
 263:     
 264:     /**
 265:      * Returns the x coordinate for the text anchor point (measured against the
 266:      * domain axis).
 267:      * 
 268:      * @return The x coordinate (in data space).
 269:      */
 270:     public double getX() {
 271:         return this.x;
 272:     }
 273:     
 274:     /**
 275:      * Sets the x coordinate for the text anchor point (measured against the 
 276:      * domain axis).
 277:      * 
 278:      * @param x  the x coordinate (in data space).
 279:      */
 280:     public void setX(double x) {
 281:         this.x = x;
 282:     }
 283:     
 284:     /**
 285:      * Returns the y coordinate for the text anchor point (measured against the
 286:      * range axis).
 287:      * 
 288:      * @return The y coordinate (in data space).
 289:      */
 290:     public double getY() {
 291:         return this.y;
 292:     }
 293:     
 294:     /**
 295:      * Sets the y coordinate for the text anchor point (measured against the
 296:      * range axis).
 297:      * 
 298:      * @param y  the y coordinate.
 299:      */
 300:     public void setY(double y) {
 301:         this.y = y;
 302:     }    
 303: 
 304:     /**
 305:      * Draws the annotation.
 306:      *
 307:      * @param g2  the graphics device.
 308:      * @param plot  the plot.
 309:      * @param dataArea  the data area.
 310:      * @param domainAxis  the domain axis.
 311:      * @param rangeAxis  the range axis.
 312:      * @param rendererIndex  the renderer index.
 313:      * @param info  an optional info object that will be populated with
 314:      *              entity information.
 315:      */
 316:     public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
 317:                      ValueAxis domainAxis, ValueAxis rangeAxis, 
 318:                      int rendererIndex,
 319:                      PlotRenderingInfo info) {
 320: 
 321:         PlotOrientation orientation = plot.getOrientation();
 322:         RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
 323:             plot.getDomainAxisLocation(), orientation
 324:         );
 325:         RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
 326:             plot.getRangeAxisLocation(), orientation
 327:         );
 328: 
 329:         float anchorX = (float) domainAxis.valueToJava2D(
 330:             this.x, dataArea, domainEdge
 331:         );
 332:         float anchorY = (float) rangeAxis.valueToJava2D(
 333:             this.y, dataArea, rangeEdge
 334:         );
 335: 
 336:         if (orientation == PlotOrientation.HORIZONTAL) {
 337:             float tempAnchor = anchorX;
 338:             anchorX = anchorY;
 339:             anchorY = tempAnchor;
 340:         }
 341:         
 342:         g2.setFont(getFont());
 343:         g2.setPaint(getPaint());
 344:         TextUtilities.drawRotatedString(
 345:             getText(), 
 346:             g2,
 347:             anchorX, 
 348:             anchorY,
 349:             getTextAnchor(),
 350:             getRotationAngle(),
 351:             getRotationAnchor()
 352:         );
 353:         Shape hotspot = TextUtilities.calculateRotatedStringBounds(
 354:             getText(), 
 355:             g2,
 356:             anchorX, 
 357:             anchorY,
 358:             getTextAnchor(),
 359:             getRotationAngle(),
 360:             getRotationAnchor()
 361:         );
 362:         
 363:         String toolTip = getToolTipText();
 364:         String url = getURL();
 365:         if (toolTip != null || url != null) {
 366:             addEntity(info, hotspot, rendererIndex, toolTip, url);
 367:         }
 368: 
 369:     }
 370:     
 371:     /**
 372:      * Tests this annotation for equality with an arbitrary object.
 373:      * 
 374:      * @param obj  the object (<code>null</code> permitted).
 375:      * 
 376:      * @return A boolean.
 377:      */
 378:     public boolean equals(Object obj) {
 379:         if (obj == this) {
 380:             return true;   
 381:         }
 382:         if (!(obj instanceof XYTextAnnotation)) {
 383:             return false;   
 384:         }
 385:         if (!super.equals(obj)) {
 386:             return false;
 387:         }
 388:         XYTextAnnotation that = (XYTextAnnotation) obj;
 389:         if (!this.text.equals(that.text)) {
 390:             return false;   
 391:         }
 392:         if (this.x != that.x) {
 393:             return false;
 394:         }
 395:         if (this.y != that.y) {
 396:             return false;
 397:         }
 398:         if (!this.font.equals(that.font)) {
 399:             return false;   
 400:         }
 401:         if (!PaintUtilities.equal(this.paint, that.paint)) {
 402:             return false;   
 403:         }
 404:         if (!this.rotationAnchor.equals(that.rotationAnchor)) {
 405:             return false;   
 406:         }
 407:         if (this.rotationAngle != that.rotationAngle) {
 408:             return false;   
 409:         }
 410:         if (!this.textAnchor.equals(that.textAnchor)) {
 411:             return false;   
 412:         }
 413:         return true;   
 414:     }
 415:     
 416:     /**
 417:      * Returns a hash code for the object.
 418:      * 
 419:      * @return A hash code.
 420:      */
 421:     public int hashCode() {
 422:         // TODO: implement this properly.
 423:         return this.text.hashCode();   
 424:     }
 425:     
 426:     /**
 427:      * Returns a clone of the annotation.
 428:      * 
 429:      * @return A clone.
 430:      * 
 431:      * @throws CloneNotSupportedException  if the annotation can't be cloned.
 432:      */
 433:     public Object clone() throws CloneNotSupportedException {
 434:         return super.clone();
 435:     }
 436:     
 437:     /**
 438:      * Provides serialization support.
 439:      *
 440:      * @param stream  the output stream.
 441:      *
 442:      * @throws IOException  if there is an I/O error.
 443:      */
 444:     private void writeObject(ObjectOutputStream stream) throws IOException {
 445:         stream.defaultWriteObject();
 446:         SerialUtilities.writePaint(this.paint, stream);
 447:     }
 448: 
 449:     /**
 450:      * Provides serialization support.
 451:      *
 452:      * @param stream  the input stream.
 453:      *
 454:      * @throws IOException  if there is an I/O error.
 455:      * @throws ClassNotFoundException  if there is a classpath problem.
 456:      */
 457:     private void readObject(ObjectInputStream stream) 
 458:         throws IOException, ClassNotFoundException {
 459:         stream.defaultReadObject();
 460:         this.paint = SerialUtilities.readPaint(stream);
 461:     }
 462: 
 463: 
 464: }