001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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-2007, 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.2 2007/02/06 11:28:53 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 * ------------- JFREECHART 1.0.x --------------------------------------------- 065 * 06-Feb-2007 : API doc update (DG); 066 * 067 */ 068 069 package org.jfree.chart.entity; 070 071 import java.awt.Shape; 072 import java.awt.geom.PathIterator; 073 import java.awt.geom.Rectangle2D; 074 import java.io.IOException; 075 import java.io.ObjectInputStream; 076 import java.io.ObjectOutputStream; 077 import java.io.Serializable; 078 079 import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator; 080 import org.jfree.chart.imagemap.URLTagFragmentGenerator; 081 import org.jfree.io.SerialUtilities; 082 import org.jfree.util.ObjectUtilities; 083 import org.jfree.util.PublicCloneable; 084 085 /** 086 * A class that captures information about some component of a chart (a bar, 087 * line etc). 088 */ 089 public class ChartEntity implements Cloneable, PublicCloneable, Serializable { 090 091 /** For serialization. */ 092 private static final long serialVersionUID = -4445994133561919083L; 093 094 /** The area occupied by the entity (in Java 2D space). */ 095 private transient Shape area; 096 097 /** The tool tip text for the entity. */ 098 private String toolTipText; 099 100 /** The URL text for the entity. */ 101 private String urlText; 102 103 /** 104 * Creates a new chart entity. 105 * 106 * @param area the area (<code>null</code> not permitted). 107 */ 108 public ChartEntity(Shape area) { 109 // defer argument checks... 110 this(area, null); 111 } 112 113 /** 114 * Creates a new chart entity. 115 * 116 * @param area the area (<code>null</code> not permitted). 117 * @param toolTipText the tool tip text (<code>null</code> permitted). 118 */ 119 public ChartEntity(Shape area, String toolTipText) { 120 // defer argument checks... 121 this(area, toolTipText, null); 122 } 123 124 /** 125 * Creates a new entity. 126 * 127 * @param area the area (<code>null</code> not permitted). 128 * @param toolTipText the tool tip text (<code>null</code> permitted). 129 * @param urlText the URL text for HTML image maps (<code>null</code> 130 * permitted). 131 */ 132 public ChartEntity(Shape area, String toolTipText, String urlText) { 133 if (area == null) { 134 throw new IllegalArgumentException("Null 'area' argument."); 135 } 136 this.area = area; 137 this.toolTipText = toolTipText; 138 this.urlText = urlText; 139 } 140 141 /** 142 * Returns the area occupied by the entity (in Java 2D space). 143 * 144 * @return The area (never <code>null</code>). 145 */ 146 public Shape getArea() { 147 return this.area; 148 } 149 150 /** 151 * Sets the area for the entity. 152 * <P> 153 * This class conveys information about chart entities back to a client. 154 * Setting this area doesn't change the entity (which has already been 155 * drawn). 156 * 157 * @param area the area (<code>null</code> not permitted). 158 */ 159 public void setArea(Shape area) { 160 if (area == null) { 161 throw new IllegalArgumentException("Null 'area' argument."); 162 } 163 this.area = area; 164 } 165 166 /** 167 * Returns the tool tip text for the entity. 168 * 169 * @return The tool tip text (possibly <code>null</code>). 170 */ 171 public String getToolTipText() { 172 return this.toolTipText; 173 } 174 175 /** 176 * Sets the tool tip text. 177 * 178 * @param text the text (<code>null</code> permitted). 179 */ 180 public void setToolTipText(String text) { 181 this.toolTipText = text; 182 } 183 184 /** 185 * Returns the URL text for the entity. 186 * 187 * @return The URL text (possibly <code>null</code>). 188 */ 189 public String getURLText() { 190 return this.urlText; 191 } 192 193 /** 194 * Sets the URL text. 195 * 196 * @param text the text (<code>null</code> permitted). 197 */ 198 public void setURLText(String text) { 199 this.urlText = text; 200 } 201 202 /** 203 * Returns a string describing the entity area. This string is intended 204 * for use in an AREA tag when generating an image map. 205 * 206 * @return The shape type (never <code>null</code>). 207 */ 208 public String getShapeType() { 209 if (this.area instanceof Rectangle2D) { 210 return "rect"; 211 } 212 else { 213 return "poly"; 214 } 215 } 216 217 /** 218 * Returns the shape coordinates as a string. 219 * 220 * @return The shape coordinates (never <code>null</code>). 221 */ 222 public String getShapeCoords() { 223 if (this.area instanceof Rectangle2D) { 224 return getRectCoords((Rectangle2D) this.area); 225 } 226 else { 227 return getPolyCoords(this.area); 228 } 229 } 230 231 /** 232 * Returns a string containing the coordinates (x1, y1, x2, y2) for a given 233 * rectangle. This string is intended for use in an image map. 234 * 235 * @param rectangle the rectangle (<code>null</code> not permitted). 236 * 237 * @return Upper left and lower right corner of a rectangle. 238 */ 239 private String getRectCoords(Rectangle2D rectangle) { 240 if (rectangle == null) { 241 throw new IllegalArgumentException("Null 'rectangle' argument."); 242 } 243 int x1 = (int) rectangle.getX(); 244 int y1 = (int) rectangle.getY(); 245 int x2 = x1 + (int) rectangle.getWidth(); 246 int y2 = y1 + (int) rectangle.getHeight(); 247 // fix by rfuller 248 if (x2 == x1) { 249 x2++; 250 } 251 if (y2 == y1) { 252 y2++; 253 } 254 // end fix by rfuller 255 return x1 + "," + y1 + "," + x2 + "," + y2; 256 } 257 258 /** 259 * Returns a string containing the coordinates for a given shape. This 260 * string is intended for use in an image map. 261 * 262 * @param shape the shape (<code>null</code> not permitted). 263 * 264 * @return The coordinates for a given shape as string. 265 */ 266 private String getPolyCoords(Shape shape) { 267 if (shape == null) { 268 throw new IllegalArgumentException("Null 'shape' argument."); 269 } 270 StringBuffer result = new StringBuffer(); 271 boolean first = true; 272 float[] coords = new float[6]; 273 PathIterator pi = shape.getPathIterator(null, 1.0); 274 while (!pi.isDone()) { 275 pi.currentSegment(coords); 276 if (first) { 277 first = false; 278 result.append((int) coords[0]); 279 result.append(",").append((int) coords[1]); 280 } 281 else { 282 result.append(","); 283 result.append((int) coords[0]); 284 result.append(","); 285 result.append((int) coords[1]); 286 } 287 pi.next(); 288 } 289 return result.toString(); 290 } 291 292 /** 293 * Returns an HTML image map tag for this entity. The returned fragment 294 * should be <code>XHTML 1.0</code> compliant. 295 * 296 * @param toolTipTagFragmentGenerator a generator for the HTML fragment 297 * that will contain the tooltip text (<code>null</code> not permitted 298 * if this entity contains tooltip information). 299 * @param urlTagFragmentGenerator a generator for the HTML fragment that 300 * will contain the URL reference (<code>null</code> not permitted if 301 * this entity has a URL). 302 * 303 * @return The HTML tag. 304 */ 305 public String getImageMapAreaTag( 306 ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, 307 URLTagFragmentGenerator urlTagFragmentGenerator) { 308 309 StringBuffer tag = new StringBuffer(); 310 boolean hasURL = (this.urlText == null ? false 311 : !this.urlText.equals("")); 312 boolean hasToolTip = (this.toolTipText == null ? false 313 : !this.toolTipText.equals("")); 314 if (hasURL || hasToolTip) { 315 tag.append("<area shape=\"" + getShapeType() + "\"" + " coords=\"" 316 + getShapeCoords() + "\""); 317 if (hasToolTip) { 318 tag.append(toolTipTagFragmentGenerator.generateToolTipFragment( 319 this.toolTipText)); 320 } 321 if (hasURL) { 322 tag.append(urlTagFragmentGenerator.generateURLFragment( 323 this.urlText)); 324 } 325 // if there is a tool tip, we expect it to generate the title and 326 // alt values, so we only add an empty alt if there is no tooltip 327 if (!hasToolTip) { 328 tag.append(" alt=\"\""); 329 } 330 tag.append("/>"); 331 } 332 return tag.toString(); 333 } 334 335 /** 336 * Returns a string representation of the chart entity, useful for 337 * debugging. 338 * 339 * @return A string. 340 */ 341 public String toString() { 342 StringBuffer buf = new StringBuffer("ChartEntity: "); 343 buf.append("tooltip = "); 344 buf.append(this.toolTipText); 345 return buf.toString(); 346 } 347 348 /** 349 * Tests the entity for equality with an arbitrary object. 350 * 351 * @param obj the object to test against (<code>null</code> permitted). 352 * 353 * @return A boolean. 354 */ 355 public boolean equals(Object obj) { 356 if (obj == this) { 357 return true; 358 } 359 if (obj instanceof ChartEntity) { 360 ChartEntity that = (ChartEntity) obj; 361 if (!this.area.equals(that.area)) { 362 return false; 363 } 364 if (!ObjectUtilities.equal(this.toolTipText, that.toolTipText)) { 365 return false; 366 } 367 if (!ObjectUtilities.equal(this.urlText, that.urlText)) { 368 return false; 369 } 370 return true; 371 } 372 return false; 373 } 374 375 /** 376 * Returns a clone of the entity. 377 * 378 * @return A clone. 379 * 380 * @throws CloneNotSupportedException if there is a problem cloning the 381 * entity. 382 */ 383 public Object clone() throws CloneNotSupportedException { 384 return super.clone(); 385 } 386 387 /** 388 * Provides serialization support. 389 * 390 * @param stream the output stream. 391 * 392 * @throws IOException if there is an I/O error. 393 */ 394 private void writeObject(ObjectOutputStream stream) throws IOException { 395 stream.defaultWriteObject(); 396 SerialUtilities.writeShape(this.area, stream); 397 } 398 399 /** 400 * Provides serialization support. 401 * 402 * @param stream the input stream. 403 * 404 * @throws IOException if there is an I/O error. 405 * @throws ClassNotFoundException if there is a classpath problem. 406 */ 407 private void readObject(ObjectInputStream stream) 408 throws IOException, ClassNotFoundException { 409 stream.defaultReadObject(); 410 this.area = SerialUtilities.readShape(stream); 411 } 412 413 }