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 * ChartEntity.java 029 * ---------------- 030 * (C) Copyright 2002-2005, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Richard Atkinson; 034 * Xavier Poinsard; 035 * Robert Fuller; 036 * 037 * $Id: ChartEntity.java,v 1.8.2.1 2005/10/25 20:41:59 mungady Exp $ 038 * 039 * Changes: 040 * -------- 041 * 23-May-2002 : Version 1 (DG); 042 * 12-Jun-2002 : Added Javadoc comments (DG); 043 * 26-Jun-2002 : Added methods for image maps (DG); 044 * 05-Aug-2002 : Added constructor and accessors for URL support in image maps 045 * Added getImageMapAreaTag() - previously in subclasses (RA); 046 * 05-Sep-2002 : Added getImageMapAreaTag(boolean) to support OverLIB for 047 * tooltips http://www.bosrup.com/web/overlib (RA); 048 * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG); 049 * 08-Oct-2002 : Changed getImageMapAreaTag to use title instead of alt 050 * attribute so HTML image maps now work in Mozilla and Opera as 051 * well as Internet Explorer (RA); 052 * 13-Mar-2003 : Change getImageMapAreaTag to only return a tag when there is a 053 * tooltip or URL, as suggested by Xavier Poinsard (see Feature 054 * Request 688079) (DG); 055 * 12-Aug-2003 : Added support for custom image maps using 056 * ToolTipTagFragmentGenerator and URLTagFragmentGenerator (RA); 057 * 02-Sep-2003 : Incorporated fix (791901) submitted by Robert Fuller (DG); 058 * 19-May-2004 : Added equals() method and implemented Cloneable and 059 * Serializable (DG); 060 * 29-Sep-2004 : Implemented PublicCloneable (DG); 061 * 13-Jan-2005 : Fixed for compliance with XHTML 1.0 (DG); 062 * 18-Apr-2005 : Use StringBuffer (DG); 063 * 20-Apr-2005 : Added toString() implementation (DG); 064 * 065 */ 066 067 package org.jfree.chart.entity; 068 069 import java.awt.Shape; 070 import java.awt.geom.PathIterator; 071 import java.awt.geom.Rectangle2D; 072 import java.io.IOException; 073 import java.io.ObjectInputStream; 074 import java.io.ObjectOutputStream; 075 import java.io.Serializable; 076 077 import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator; 078 import org.jfree.chart.imagemap.URLTagFragmentGenerator; 079 import org.jfree.io.SerialUtilities; 080 import org.jfree.util.ObjectUtilities; 081 import org.jfree.util.PublicCloneable; 082 083 /** 084 * A class that captures information about some component of a chart (a bar, 085 * line etc). 086 */ 087 public class ChartEntity implements Cloneable, PublicCloneable, Serializable { 088 089 /** For serialization. */ 090 private static final long serialVersionUID = -4445994133561919083L; 091 092 /** The area occupied by the entity (in Java 2D space). */ 093 private transient Shape area; 094 095 /** The tool tip text for the entity. */ 096 private String toolTipText; 097 098 /** The URL text for the entity. */ 099 private String urlText; 100 101 /** 102 * Creates a new chart entity. 103 * 104 * @param area the area (<code>null</code> not permitted). 105 */ 106 public ChartEntity(Shape area) { 107 // defer argument checks... 108 this(area, null); 109 } 110 111 /** 112 * Creates a new chart entity. 113 * 114 * @param area the area (<code>null</code> not permitted). 115 * @param toolTipText the tool tip text (<code>null</code> permitted). 116 */ 117 public ChartEntity(Shape area, String toolTipText) { 118 // defer argument checks... 119 this(area, toolTipText, null); 120 } 121 122 /** 123 * Creates a new entity. 124 * 125 * @param area the area (<code>null</code> not permitted). 126 * @param toolTipText the tool tip text (<code>null</code> permitted). 127 * @param urlText the URL text for HTML image maps (<code>null</code> 128 * permitted). 129 */ 130 public ChartEntity(Shape area, String toolTipText, String urlText) { 131 if (area == null) { 132 throw new IllegalArgumentException("Null 'area' argument."); 133 } 134 this.area = area; 135 this.toolTipText = toolTipText; 136 this.urlText = urlText; 137 } 138 139 /** 140 * Returns the area occupied by the entity (in Java 2D space). 141 * 142 * @return The area (never <code>null</code>). 143 */ 144 public Shape getArea() { 145 return this.area; 146 } 147 148 /** 149 * Sets the area for the entity. 150 * <P> 151 * This class conveys information about chart entities back to a client. 152 * Setting this area doesn't change the entity (which has already been 153 * drawn). 154 * 155 * @param area the area (<code>null</code> not permitted). 156 */ 157 public void setArea(Shape area) { 158 if (area == null) { 159 throw new IllegalArgumentException("Null 'area' argument."); 160 } 161 this.area = area; 162 } 163 164 /** 165 * Returns the tool tip text for the entity. 166 * 167 * @return The tool tip text (possibly <code>null</code>). 168 */ 169 public String getToolTipText() { 170 return this.toolTipText; 171 } 172 173 /** 174 * Sets the tool tip text. 175 * 176 * @param text the text (<code>null</code> permitted). 177 */ 178 public void setToolTipText(String text) { 179 this.toolTipText = text; 180 } 181 182 /** 183 * Returns the URL text for the entity. 184 * 185 * @return The URL text (possibly <code>null</code>). 186 */ 187 public String getURLText() { 188 return this.urlText; 189 } 190 191 /** 192 * Sets the URL text. 193 * 194 * @param text the text (<code>null</code> permitted). 195 */ 196 public void setURLText(String text) { 197 this.urlText = text; 198 } 199 200 /** 201 * Returns a string describing the entity area. This string is intended 202 * for use in an AREA tag when generating an image map. 203 * 204 * @return The shape type (never <code>null</code>). 205 */ 206 public String getShapeType() { 207 if (this.area instanceof Rectangle2D) { 208 return "rect"; 209 } 210 else { 211 return "poly"; 212 } 213 } 214 215 /** 216 * Returns the shape coordinates as a string. 217 * 218 * @return The shape coordinates (never <code>null</code>). 219 */ 220 public String getShapeCoords() { 221 if (this.area instanceof Rectangle2D) { 222 return getRectCoords((Rectangle2D) this.area); 223 } 224 else { 225 return getPolyCoords(this.area); 226 } 227 } 228 229 /** 230 * Returns a string containing the coordinates (x1, y1, x2, y2) for a given 231 * rectangle. This string is intended for use in an image map. 232 * 233 * @param rectangle the rectangle (<code>null</code> not permitted). 234 * 235 * @return Upper left and lower right corner of a rectangle. 236 */ 237 private String getRectCoords(Rectangle2D rectangle) { 238 if (rectangle == null) { 239 throw new IllegalArgumentException("Null 'rectangle' argument."); 240 } 241 int x1 = (int) rectangle.getX(); 242 int y1 = (int) rectangle.getY(); 243 int x2 = x1 + (int) rectangle.getWidth(); 244 int y2 = y1 + (int) rectangle.getHeight(); 245 // fix by rfuller 246 if (x2 == x1) { 247 x2++; 248 } 249 if (y2 == y1) { 250 y2++; 251 } 252 // end fix by rfuller 253 return x1 + "," + y1 + "," + x2 + "," + y2; 254 } 255 256 /** 257 * Returns a string containing the coordinates for a given shape. This 258 * string is intended for use in an image map. 259 * 260 * @param shape the shape (<code>null</code> not permitted). 261 * 262 * @return The coordinates for a given shape as string. 263 */ 264 private String getPolyCoords(Shape shape) { 265 if (shape == null) { 266 throw new IllegalArgumentException("Null 'shape' argument."); 267 } 268 StringBuffer result = new StringBuffer(); 269 boolean first = true; 270 float[] coords = new float[6]; 271 PathIterator pi = shape.getPathIterator(null, 1.0); 272 while (!pi.isDone()) { 273 pi.currentSegment(coords); 274 if (first) { 275 first = false; 276 result.append((int) coords[0]); 277 result.append(",").append((int) coords[1]); 278 } 279 else { 280 result.append(","); 281 result.append((int) coords[0]); 282 result.append(","); 283 result.append((int) coords[1]); 284 } 285 pi.next(); 286 } 287 return result.toString(); 288 } 289 290 /** 291 * Returns an HTML image map tag for this entity. The returned fragment 292 * should be <code>XHTML 1.0</code> compliant. 293 * 294 * @param toolTipTagFragmentGenerator the generator for tooltip fragment. 295 * @param urlTagFragmentGenerator the generator for the URL fragment. 296 * 297 * @return The HTML tag. 298 */ 299 public String getImageMapAreaTag( 300 ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, 301 URLTagFragmentGenerator urlTagFragmentGenerator) { 302 303 StringBuffer tag = new StringBuffer(); 304 boolean hasURL 305 = (this.urlText == null ? false : !this.urlText.equals("")); 306 boolean hasToolTip 307 = (this.toolTipText == null ? false : !this.toolTipText.equals("")); 308 if (hasURL || hasToolTip) { 309 tag.append( 310 "<area shape=\"" + getShapeType() + "\"" + " coords=\"" 311 + getShapeCoords() + "\"" 312 ); 313 if (hasToolTip) { 314 tag.append(toolTipTagFragmentGenerator.generateToolTipFragment( 315 this.toolTipText 316 )); 317 } 318 if (hasURL) { 319 tag.append( 320 urlTagFragmentGenerator.generateURLFragment(this.urlText) 321 ); 322 } 323 // if there is a tool tip, we expect it to generate the title and 324 // alt values, so we only add an empty alt if there is no tooltip 325 if (!hasToolTip) { 326 tag.append(" alt=\"\""); 327 } 328 tag.append("/>"); 329 } 330 return tag.toString(); 331 } 332 333 /** 334 * Returns a string representation of the chart entity, useful for 335 * debugging. 336 * 337 * @return A string. 338 */ 339 public String toString() { 340 StringBuffer buf = new StringBuffer("ChartEntity: "); 341 buf.append("tooltip = "); 342 buf.append(this.toolTipText); 343 return buf.toString(); 344 } 345 346 /** 347 * Tests the entity for equality with an arbitrary object. 348 * 349 * @param obj the object to test against (<code>null</code> permitted). 350 * 351 * @return A boolean. 352 */ 353 public boolean equals(Object obj) { 354 if (obj == this) { 355 return true; 356 } 357 if (obj instanceof ChartEntity) { 358 ChartEntity that = (ChartEntity) obj; 359 if (!this.area.equals(that.area)) { 360 return false; 361 } 362 if (!ObjectUtilities.equal(this.toolTipText, that.toolTipText)) { 363 return false; 364 } 365 if (!ObjectUtilities.equal(this.urlText, that.urlText)) { 366 return false; 367 } 368 return true; 369 } 370 return false; 371 } 372 373 /** 374 * Returns a clone of the entity. 375 * 376 * @return A clone. 377 * 378 * @throws CloneNotSupportedException if there is a problem cloning the 379 * entity. 380 */ 381 public Object clone() throws CloneNotSupportedException { 382 return super.clone(); 383 } 384 385 /** 386 * Provides serialization support. 387 * 388 * @param stream the output stream. 389 * 390 * @throws IOException if there is an I/O error. 391 */ 392 private void writeObject(ObjectOutputStream stream) throws IOException { 393 stream.defaultWriteObject(); 394 SerialUtilities.writeShape(this.area, stream); 395 } 396 397 /** 398 * Provides serialization support. 399 * 400 * @param stream the input stream. 401 * 402 * @throws IOException if there is an I/O error. 403 * @throws ClassNotFoundException if there is a classpath problem. 404 */ 405 private void readObject(ObjectInputStream stream) 406 throws IOException, ClassNotFoundException { 407 stream.defaultReadObject(); 408 this.area = SerialUtilities.readShape(stream); 409 } 410 411 }