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}