    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * -------------------
    028 * TextAnnotation.java
    029 * -------------------
    030 * (C) Copyright 2002-2011, by Object Refinery Limited and Contributors.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   Peter Kolb (patch 2809117);
    034 *                   Martin Hoeller;
    035 *
    036 * Changes:
    037 * --------
    038 * 28-Aug-2002 : Version 1 (DG);
    039 * 07-Nov-2002 : Fixed errors reported by Checkstyle, added accessor
    040 *               methods (DG);
    041 * 13-Jan-2003 : Reviewed Javadocs (DG);
    042 * 26-Mar-2003 : Implemented Serializable (DG);
    043 * 02-Jun-2003 : Added anchor and rotation settings (DG);
    044 * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
    045 * 29-Sep-2004 : Updated equals() method (DG);
    046 * 06-Jun-2005 : Fixed equals() method to work with GradientPaint (DG);
    047 * ------------- JFREECHART 1.0.x ---------------------------------------------
    048 * 16-Jan-2007 : Added argument checks, fixed hashCode() method and updated
    049 *               API docs (DG);
    050 * 24-Jun-2009 : Fire change events (see patch 2809117 by PK) (DG);
    051 * 28-Oct-2011 : Added missing argument check, Bug #3428870 (MH);
    052 *
    053 */
    054
    055package org.jfree.chart.annotations;
    056
    057import java.awt.Color;
    058import java.awt.Font;
    059import java.awt.Paint;
    060import java.io.IOException;
    061import java.io.ObjectInputStream;
    062import java.io.ObjectOutputStream;
    063import java.io.Serializable;
    064
    065import org.jfree.chart.HashUtilities;
    066import org.jfree.chart.event.AnnotationChangeEvent;
    067import org.jfree.io.SerialUtilities;
    068import org.jfree.ui.TextAnchor;
    069import org.jfree.util.ObjectUtilities;
    070import org.jfree.util.PaintUtilities;
    071
    072/**
    073 * A base class for text annotations.  This class records the content but not
    074 * the location of the annotation.
    075 */
    076public class TextAnnotation extends AbstractAnnotation implements Serializable {
    077
    078    /** For serialization. */
    079    private static final long serialVersionUID = 7008912287533127432L;
    080
    081    /** The default font. */
    082    public static final Font DEFAULT_FONT
    083            = new Font("SansSerif", Font.PLAIN, 10);
    084
    085    /** The default paint. */
    086    public static final Paint DEFAULT_PAINT = Color.black;
    087
    088    /** The default text anchor. */
    089    public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER;
    090
    091    /** The default rotation anchor. */
    092    public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER;
    093
    094    /** The default rotation angle. */
    095    public static final double DEFAULT_ROTATION_ANGLE = 0.0;
    096
    097    /** The text. */
    098    private String text;
    099
    100    /** The font. */
    101    private Font font;
    102
    103    /** The paint. */
    104    private transient Paint paint;
    105
    106    /** The text anchor. */
    107    private TextAnchor textAnchor;
    108
    109    /** The rotation anchor. */
    110    private TextAnchor rotationAnchor;
    111
    112    /** The rotation angle. */
    113    private double rotationAngle;
    114
    115    /**
    116     * Creates a text annotation with default settings.
    117     *
    118     * @param text  the text (<code>null</code> not permitted).
    119     */
    120    protected TextAnnotation(String text) {
    121        super();
    122        if (text == null) {
    123            throw new IllegalArgumentException("Null 'text' argument.");
    124        }
    125        this.text = text;
    126        this.font = DEFAULT_FONT;
    127        this.paint = DEFAULT_PAINT;
    128        this.textAnchor = DEFAULT_TEXT_ANCHOR;
    129        this.rotationAnchor = DEFAULT_ROTATION_ANCHOR;
    130        this.rotationAngle = DEFAULT_ROTATION_ANGLE;
    131    }
    132
    133    /**
    134     * Returns the text for the annotation.
    135     *
    136     * @return The text (never <code>null</code>).
    137     *
    138     * @see #setText(String)
    139     */
    140    public String getText() {
    141        return this.text;
    142    }
    143
    144    /**
    145     * Sets the text for the annotation.
    146     *
    147     * @param text  the text (<code>null</code> not permitted).
    148     *
    149     * @see #getText()
    150     */
    151    public void setText(String text) {
    152        if (text == null) {
    153            throw new IllegalArgumentException("Null 'text' argument.");
    154        }
    155        this.text = text;
    156    }
    157
    158    /**
    159     * Returns the font for the annotation.
    160     *
    161     * @return The font (never <code>null</code>).
    162     *
    163     * @see #setFont(Font)
    164     */
    165    public Font getFont() {
    166        return this.font;
    167    }
    168
    169    /**
    170     * Sets the font for the annotation and sends an
    171     * {@link AnnotationChangeEvent} to all registered listeners.
    172     *
    173     * @param font  the font (<code>null</code> not permitted).
    174     *
    175     * @see #getFont()
    176     */
    177    public void setFont(Font font) {
    178        if (font == null) {
    179            throw new IllegalArgumentException("Null 'font' argument.");
    180        }
    181        this.font = font;
    182        fireAnnotationChanged();
    183    }
    184
    185    /**
    186     * Returns the paint for the annotation.
    187     *
    188     * @return The paint (never <code>null</code>).
    189     *
    190     * @see #setPaint(Paint)
    191     */
    192    public Paint getPaint() {
    193        return this.paint;
    194    }
    195
    196    /**
    197     * Sets the paint for the annotation and sends an
    198     * {@link AnnotationChangeEvent} to all registered listeners.
    199     *
    200     * @param paint  the paint (<code>null</code> not permitted).
    201     *
    202     * @see #getPaint()
    203     */
    204    public void setPaint(Paint paint) {
    205        if (paint == null) {
    206            throw new IllegalArgumentException("Null 'paint' argument.");
    207        }
    208        this.paint = paint;
    209        fireAnnotationChanged();
    210    }
    211
    212    /**
    213     * Returns the text anchor.
    214     *
    215     * @return The text anchor.
    216     *
    217     * @see #setTextAnchor(TextAnchor)
    218     */
    219    public TextAnchor getTextAnchor() {
    220        return this.textAnchor;
    221    }
    222
    223    /**
    224     * Sets the text anchor (the point on the text bounding rectangle that is
    225     * aligned to the (x, y) coordinate of the annotation) and sends an
    226     * {@link AnnotationChangeEvent} to all registered listeners.
    227     *
    228     * @param anchor  the anchor point (<code>null</code> not permitted).
    229     *
    230     * @see #getTextAnchor()
    231     */
    232    public void setTextAnchor(TextAnchor anchor) {
    233        if (anchor == null) {
    234            throw new IllegalArgumentException("Null 'anchor' argument.");
    235        }
    236        this.textAnchor = anchor;
    237        fireAnnotationChanged();
    238    }
    239
    240    /**
    241     * Returns the rotation anchor.
    242     *
    243     * @return The rotation anchor point (never <code>null</code>).
    244     *
    245     * @see #setRotationAnchor(TextAnchor)
    246     */
    247    public TextAnchor getRotationAnchor() {
    248        return this.rotationAnchor;
    249    }
    250
    251    /**
    252     * Sets the rotation anchor point and sends an
    253     * {@link AnnotationChangeEvent} to all registered listeners.
    254     *
    255     * @param anchor  the anchor (<code>null</code> not permitted).
    256     *
    257     * @see #getRotationAnchor()
    258     */
    259    public void setRotationAnchor(TextAnchor anchor) {
    260        if (anchor == null) {
    261            throw new IllegalArgumentException("Null 'anchor' argument.");
    262        }
    263        this.rotationAnchor = anchor;
    264        fireAnnotationChanged();
    265    }
    266
    267    /**
    268     * Returns the rotation angle in radians.
    269     *
    270     * @return The rotation angle.
    271     *
    272     * @see #setRotationAngle(double)
    273     */
    274    public double getRotationAngle() {
    275        return this.rotationAngle;
    276    }
    277
    278    /**
    279     * Sets the rotation angle and sends an {@link AnnotationChangeEvent} to
    280     * all registered listeners.  The angle is measured clockwise in radians.
    281     *
    282     * @param angle  the angle (in radians).
    283     *
    284     * @see #getRotationAngle()
    285     */
    286    public void setRotationAngle(double angle) {
    287        this.rotationAngle = angle;
    288        fireAnnotationChanged();
    289    }
    290
    291    /**
    292     * Tests this object for equality with an arbitrary object.
    293     *
    294     * @param obj  the object (<code>null</code> permitted).
    295     *
    296     * @return <code>true</code> or <code>false</code>.
    297     */
    298    public boolean equals(Object obj) {
    299        if (obj == this) {
    300            return true;
    301        }
    302        // now try to reject equality...
    303        if (!(obj instanceof TextAnnotation)) {
    304            return false;
    305        }
    306        TextAnnotation that = (TextAnnotation) obj;
    307        if (!ObjectUtilities.equal(this.text, that.getText())) {
    308            return false;
    309        }
    310        if (!ObjectUtilities.equal(this.font, that.getFont())) {
    311            return false;
    312        }
    313        if (!PaintUtilities.equal(this.paint, that.getPaint())) {
    314            return false;
    315        }
    316        if (!ObjectUtilities.equal(this.textAnchor, that.getTextAnchor())) {
    317            return false;
    318        }
    319        if (!ObjectUtilities.equal(this.rotationAnchor,
    320                that.getRotationAnchor())) {
    321            return false;
    322        }
    323        if (this.rotationAngle != that.getRotationAngle()) {
    324            return false;
    325        }
    326
    327        // seem to be the same...
    328        return true;
    329
    330    }
    331
    332    /**
    333     * Returns a hash code for this instance.
    334     *
    335     * @return A hash code.
    336     */
    337    public int hashCode() {
    338        int result = 193;
    339        result = 37 * result + this.font.hashCode();
    340        result = 37 * result + HashUtilities.hashCodeForPaint(this.paint);
    341        result = 37 * result + this.rotationAnchor.hashCode();
    342        long temp = Double.doubleToLongBits(this.rotationAngle);
    343        result = 37 * result + (int) (temp ^ (temp >>> 32));
    344        result = 37 * result + this.text.hashCode();
    345        result = 37 * result + this.textAnchor.hashCode();
    346        return result;
    347    }
    348
    349    /**
    350     * Provides serialization support.
    351     *
    352     * @param stream  the output stream.
    353     *
    354     * @throws IOException if there is an I/O error.
    355     */
    356    private void writeObject(ObjectOutputStream stream) throws IOException {
    357        stream.defaultWriteObject();
    358        SerialUtilities.writePaint(this.paint, stream);
    359    }
    360
    361    /**
    362     * Provides serialization support.
    363     *
    364     * @param stream  the input stream.
    365     *
    366     * @throws IOException  if there is an I/O error.
    367     * @throws ClassNotFoundException  if there is a classpath problem.
    368     */
    369    private void readObject(ObjectInputStream stream)
    370        throws IOException, ClassNotFoundException {
    371        stream.defaultReadObject();
    372        this.paint = SerialUtilities.readPaint(stream);
    373    }
    374
    375}