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