001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * CategoryTextAnnotation.java 029 * --------------------------- 030 * (C) Copyright 2003-present, by David Gilbert and Contributors. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Peter Kolb (patch 2809117); 034 * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); 035 * 036 */ 037 038package org.jfree.chart.annotations; 039 040import java.awt.Graphics2D; 041import java.awt.geom.Rectangle2D; 042import java.io.Serializable; 043import java.util.Objects; 044 045import org.jfree.chart.axis.CategoryAnchor; 046import org.jfree.chart.axis.CategoryAxis; 047import org.jfree.chart.axis.ValueAxis; 048import org.jfree.chart.event.AnnotationChangeEvent; 049import org.jfree.chart.plot.CategoryPlot; 050import org.jfree.chart.plot.Plot; 051import org.jfree.chart.plot.PlotOrientation; 052import org.jfree.chart.text.TextUtils; 053import org.jfree.chart.ui.RectangleEdge; 054import org.jfree.chart.util.Args; 055import org.jfree.chart.util.PublicCloneable; 056import org.jfree.data.category.CategoryDataset; 057 058/** 059 * A text annotation that can be placed on a {@link CategoryPlot}. 060 */ 061public class CategoryTextAnnotation extends TextAnnotation 062 implements CategoryAnnotation, Cloneable, PublicCloneable, 063 Serializable { 064 065 /** For serialization. */ 066 private static final long serialVersionUID = 3333360090781320147L; 067 068 /** The category. */ 069 private Comparable category; 070 071 /** The category anchor (START, MIDDLE, or END). */ 072 private CategoryAnchor categoryAnchor; 073 074 /** The value. */ 075 private double value; 076 077 /** 078 * Creates a new annotation to be displayed at the given location. 079 * 080 * @param text the text ({@code null} not permitted). 081 * @param category the category ({@code null} not permitted). 082 * @param value the value. 083 */ 084 public CategoryTextAnnotation(String text, Comparable category, 085 double value) { 086 super(text); 087 Args.nullNotPermitted(category, "category"); 088 this.category = category; 089 this.value = value; 090 this.categoryAnchor = CategoryAnchor.MIDDLE; 091 } 092 093 /** 094 * Returns the category. 095 * 096 * @return The category (never {@code null}). 097 * 098 * @see #setCategory(Comparable) 099 */ 100 public Comparable getCategory() { 101 return this.category; 102 } 103 104 /** 105 * Sets the category that the annotation attaches to and sends an 106 * {@link AnnotationChangeEvent} to all registered listeners. 107 * 108 * @param category the category ({@code null} not permitted). 109 * 110 * @see #getCategory() 111 */ 112 public void setCategory(Comparable category) { 113 Args.nullNotPermitted(category, "category"); 114 this.category = category; 115 fireAnnotationChanged(); 116 } 117 118 /** 119 * Returns the category anchor point. 120 * 121 * @return The category anchor point. 122 * 123 * @see #setCategoryAnchor(CategoryAnchor) 124 */ 125 public CategoryAnchor getCategoryAnchor() { 126 return this.categoryAnchor; 127 } 128 129 /** 130 * Sets the category anchor point and sends an 131 * {@link AnnotationChangeEvent} to all registered listeners. 132 * 133 * @param anchor the anchor point ({@code null} not permitted). 134 * 135 * @see #getCategoryAnchor() 136 */ 137 public void setCategoryAnchor(CategoryAnchor anchor) { 138 Args.nullNotPermitted(anchor, "anchor"); 139 this.categoryAnchor = anchor; 140 fireAnnotationChanged(); 141 } 142 143 /** 144 * Returns the value that the annotation attaches to. 145 * 146 * @return The value. 147 * 148 * @see #setValue(double) 149 */ 150 public double getValue() { 151 return this.value; 152 } 153 154 /** 155 * Sets the value and sends an 156 * {@link AnnotationChangeEvent} to all registered listeners. 157 * 158 * @param value the value. 159 * 160 * @see #getValue() 161 */ 162 public void setValue(double value) { 163 this.value = value; 164 fireAnnotationChanged(); 165 } 166 167 /** 168 * Draws the annotation. 169 * 170 * @param g2 the graphics device. 171 * @param plot the plot. 172 * @param dataArea the data area. 173 * @param domainAxis the domain axis. 174 * @param rangeAxis the range axis. 175 */ 176 @Override 177 public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, 178 CategoryAxis domainAxis, ValueAxis rangeAxis) { 179 180 CategoryDataset dataset = plot.getDataset(); 181 int catIndex = dataset.getColumnIndex(this.category); 182 int catCount = dataset.getColumnCount(); 183 184 float anchorX = 0.0f; 185 float anchorY = 0.0f; 186 PlotOrientation orientation = plot.getOrientation(); 187 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 188 plot.getDomainAxisLocation(), orientation); 189 RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( 190 plot.getRangeAxisLocation(), orientation); 191 192 if (orientation == PlotOrientation.HORIZONTAL) { 193 anchorY = (float) domainAxis.getCategoryJava2DCoordinate( 194 this.categoryAnchor, catIndex, catCount, dataArea, 195 domainEdge); 196 anchorX = (float) rangeAxis.valueToJava2D(this.value, dataArea, 197 rangeEdge); 198 } 199 else if (orientation == PlotOrientation.VERTICAL) { 200 anchorX = (float) domainAxis.getCategoryJava2DCoordinate( 201 this.categoryAnchor, catIndex, catCount, dataArea, 202 domainEdge); 203 anchorY = (float) rangeAxis.valueToJava2D(this.value, dataArea, 204 rangeEdge); 205 } 206 g2.setFont(getFont()); 207 g2.setPaint(getPaint()); 208 TextUtils.drawRotatedString(getText(), g2, anchorX, anchorY, 209 getTextAnchor(), getRotationAngle(), getRotationAnchor()); 210 211 } 212 213 /** 214 * Tests this object for equality with another. 215 * 216 * @param obj the object ({@code null} permitted). 217 * 218 * @return {@code true} or {@code false}. 219 */ 220 @Override 221 public boolean equals(Object obj) { 222 if (obj == this) { 223 return true; 224 } 225 if (!(obj instanceof CategoryTextAnnotation)) { 226 return false; 227 } 228 CategoryTextAnnotation that = (CategoryTextAnnotation) obj; 229 if (!Objects.equals(this.category, that.category)) { 230 return false; 231 } 232 if (!Objects.equals(this.categoryAnchor, that.categoryAnchor)) { 233 return false; 234 } 235 if (Double.doubleToLongBits(this.value) != 236 Double.doubleToLongBits(that.value)) { 237 return false; 238 } 239 // fix the "equals not symmetric" problem 240 if (!that.canEqual(this)) { 241 return false; 242 } 243 244 return super.equals(obj); 245 } 246 247 /** 248 * Ensures symmetry between super/subclass implementations of equals. For 249 * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. 250 * 251 * @param other Object 252 * 253 * @return true ONLY if the parameter is THIS class type 254 */ 255 @Override 256 public boolean canEqual(Object other) { 257 // fix the "equals not symmetric" problem 258 return (other instanceof CategoryTextAnnotation); 259 } 260 261 /** 262 * Returns a hash code for this instance. 263 * 264 * @return A hash code. 265 */ 266 @Override 267 public int hashCode() { 268 int result = super.hashCode(); // equals calls superclass, hashCode must also 269 result = 37 * result + Objects.hashCode(this.category); 270 result = 37 * result + Objects.hashCode(this.categoryAnchor); 271 long temp = Double.doubleToLongBits(this.value); 272 result = 37 * result + (int) (temp ^ (temp >>> 32)); 273 return result; 274 } 275 276 /** 277 * Returns a clone of the annotation. 278 * 279 * @return A clone. 280 * 281 * @throws CloneNotSupportedException this class will not throw this 282 * exception, but subclasses (if any) might. 283 */ 284 @Override 285 public Object clone() throws CloneNotSupportedException { 286 return super.clone(); 287 } 288 289}