001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2021, 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-2021, 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 */
037
038package org.jfree.chart.annotations;
039
040import java.awt.Color;
041import java.awt.Font;
042import java.awt.Paint;
043import java.io.IOException;
044import java.io.ObjectInputStream;
045import java.io.ObjectOutputStream;
046import java.io.Serializable;
047import java.util.Objects;
048
049import org.jfree.chart.HashUtils;
050import org.jfree.chart.event.AnnotationChangeEvent;
051import org.jfree.chart.ui.TextAnchor;
052import org.jfree.chart.util.PaintUtils;
053import org.jfree.chart.util.Args;
054import org.jfree.chart.util.SerialUtils;
055
056/**
057 * A base class for text annotations.  This class records the content but not
058 * the location of the annotation.
059 */
060public class TextAnnotation extends AbstractAnnotation implements Serializable {
061
062    /** For serialization. */
063    private static final long serialVersionUID = 7008912287533127432L;
064
065    /** The default font. */
066    public static final Font DEFAULT_FONT
067            = new Font("SansSerif", Font.PLAIN, 10);
068
069    /** The default paint. */
070    public static final Paint DEFAULT_PAINT = Color.BLACK;
071
072    /** The default text anchor. */
073    public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER;
074
075    /** The default rotation anchor. */
076    public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER;
077
078    /** The default rotation angle. */
079    public static final double DEFAULT_ROTATION_ANGLE = 0.0;
080
081    /** The text. */
082    private String text;
083
084    /** The font. */
085    private Font font;
086
087    /** The paint. */
088    private transient Paint paint;
089
090    /** The text anchor. */
091    private TextAnchor textAnchor;
092
093    /** The rotation anchor. */
094    private TextAnchor rotationAnchor;
095
096    /** The rotation angle. */
097    private double rotationAngle;
098
099    /**
100     * Creates a text annotation with default settings.
101     *
102     * @param text  the text ({@code null} not permitted).
103     */
104    protected TextAnnotation(String text) {
105        super();
106        Args.nullNotPermitted(text, "text");
107        this.text = text;
108        this.font = DEFAULT_FONT;
109        this.paint = DEFAULT_PAINT;
110        this.textAnchor = DEFAULT_TEXT_ANCHOR;
111        this.rotationAnchor = DEFAULT_ROTATION_ANCHOR;
112        this.rotationAngle = DEFAULT_ROTATION_ANGLE;
113    }
114
115    /**
116     * Returns the text for the annotation.
117     *
118     * @return The text (never {@code null}).
119     *
120     * @see #setText(String)
121     */
122    public String getText() {
123        return this.text;
124    }
125
126    /**
127     * Sets the text for the annotation and sends an 
128     * {@link AnnotationChangeEvent} to all registered listeners.
129     *
130     * @param text  the text ({@code null} not permitted).
131     *
132     * @see #getText()
133     */
134    public void setText(String text) {
135        Args.nullNotPermitted(text, "text");
136        this.text = text;
137        fireAnnotationChanged();
138    }
139
140    /**
141     * Returns the font for the annotation.
142     *
143     * @return The font (never {@code null}).
144     *
145     * @see #setFont(Font)
146     */
147    public Font getFont() {
148        return this.font;
149    }
150
151    /**
152     * Sets the font for the annotation and sends an
153     * {@link AnnotationChangeEvent} to all registered listeners.
154     *
155     * @param font  the font ({@code null} not permitted).
156     *
157     * @see #getFont()
158     */
159    public void setFont(Font font) {
160        Args.nullNotPermitted(font, "font");
161        this.font = font;
162        fireAnnotationChanged();
163    }
164
165    /**
166     * Returns the paint for the annotation.
167     *
168     * @return The paint (never {@code null}).
169     *
170     * @see #setPaint(Paint)
171     */
172    public Paint getPaint() {
173        return this.paint;
174    }
175
176    /**
177     * Sets the paint for the annotation and sends an
178     * {@link AnnotationChangeEvent} to all registered listeners.
179     *
180     * @param paint  the paint ({@code null} not permitted).
181     *
182     * @see #getPaint()
183     */
184    public void setPaint(Paint paint) {
185        Args.nullNotPermitted(paint, "paint");
186        this.paint = paint;
187        fireAnnotationChanged();
188    }
189
190    /**
191     * Returns the text anchor.
192     *
193     * @return The text anchor.
194     *
195     * @see #setTextAnchor(TextAnchor)
196     */
197    public TextAnchor getTextAnchor() {
198        return this.textAnchor;
199    }
200
201    /**
202     * Sets the text anchor (the point on the text bounding rectangle that is
203     * aligned to the (x, y) coordinate of the annotation) and sends an
204     * {@link AnnotationChangeEvent} to all registered listeners.
205     *
206     * @param anchor  the anchor point ({@code null} not permitted).
207     *
208     * @see #getTextAnchor()
209     */
210    public void setTextAnchor(TextAnchor anchor) {
211        Args.nullNotPermitted(anchor, "anchor");
212        this.textAnchor = anchor;
213        fireAnnotationChanged();
214    }
215
216    /**
217     * Returns the rotation anchor.
218     *
219     * @return The rotation anchor point (never {@code null}).
220     *
221     * @see #setRotationAnchor(TextAnchor)
222     */
223    public TextAnchor getRotationAnchor() {
224        return this.rotationAnchor;
225    }
226
227    /**
228     * Sets the rotation anchor point and sends an
229     * {@link AnnotationChangeEvent} to all registered listeners.
230     *
231     * @param anchor  the anchor ({@code null} not permitted).
232     *
233     * @see #getRotationAnchor()
234     */
235    public void setRotationAnchor(TextAnchor anchor) {
236        Args.nullNotPermitted(anchor, "anchor");
237        this.rotationAnchor = anchor;
238        fireAnnotationChanged();
239    }
240
241    /**
242     * Returns the rotation angle in radians.
243     *
244     * @return The rotation angle.
245     *
246     * @see #setRotationAngle(double)
247     */
248    public double getRotationAngle() {
249        return this.rotationAngle;
250    }
251
252    /**
253     * Sets the rotation angle and sends an {@link AnnotationChangeEvent} to
254     * all registered listeners.  The angle is measured clockwise in radians.
255     *
256     * @param angle  the angle (in radians).
257     *
258     * @see #getRotationAngle()
259     */
260    public void setRotationAngle(double angle) {
261        this.rotationAngle = angle;
262        fireAnnotationChanged();
263    }
264
265    /**
266     * Tests this object for equality with an arbitrary object.
267     *
268     * @param obj  the object ({@code null} permitted).
269     *
270     * @return {@code true} or {@code false}.
271     */
272    @Override
273    public boolean equals(Object obj) {
274        if (obj == this) {
275            return true;
276        }
277        // now try to reject equality...
278        if (!(obj instanceof TextAnnotation)) {
279            return false;
280        }
281        TextAnnotation that = (TextAnnotation) obj;
282        if (!Objects.equals(this.text, that.getText())) {
283            return false;
284        }
285        if (!Objects.equals(this.font, that.getFont())) {
286            return false;
287        }
288        if (!PaintUtils.equal(this.paint, that.getPaint())) {
289            return false;
290        }
291        if (!Objects.equals(this.textAnchor, that.getTextAnchor())) {
292            return false;
293        }
294        if (!Objects.equals(this.rotationAnchor,
295                that.getRotationAnchor())) {
296            return false;
297        }
298        if (this.rotationAngle != that.getRotationAngle()) {
299            return false;
300        }
301
302        // seem to be the same...
303        return true;
304
305    }
306
307    /**
308     * Returns a hash code for this instance.
309     *
310     * @return A hash code.
311     */
312    @Override
313    public int hashCode() {
314        int result = 193;
315        result = 37 * result + this.font.hashCode();
316        result = 37 * result + HashUtils.hashCodeForPaint(this.paint);
317        result = 37 * result + this.rotationAnchor.hashCode();
318        long temp = Double.doubleToLongBits(this.rotationAngle);
319        result = 37 * result + (int) (temp ^ (temp >>> 32));
320        result = 37 * result + this.text.hashCode();
321        result = 37 * result + this.textAnchor.hashCode();
322        return result;
323    }
324
325    /**
326     * Provides serialization support.
327     *
328     * @param stream  the output stream.
329     *
330     * @throws IOException if there is an I/O error.
331     */
332    private void writeObject(ObjectOutputStream stream) throws IOException {
333        stream.defaultWriteObject();
334        SerialUtils.writePaint(this.paint, stream);
335    }
336
337    /**
338     * Provides serialization support.
339     *
340     * @param stream  the input stream.
341     *
342     * @throws IOException  if there is an I/O error.
343     * @throws ClassNotFoundException  if there is a classpath problem.
344     */
345    private void readObject(ObjectInputStream stream)
346        throws IOException, ClassNotFoundException {
347        stream.defaultReadObject();
348        this.paint = SerialUtils.readPaint(stream);
349    }
350
351}