Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ---------------- 28: * ChartEntity.java 29: * ---------------- 30: * (C) Copyright 2002-2007, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): Richard Atkinson; 34: * Xavier Poinsard; 35: * Robert Fuller; 36: * 37: * Changes: 38: * -------- 39: * 23-May-2002 : Version 1 (DG); 40: * 12-Jun-2002 : Added Javadoc comments (DG); 41: * 26-Jun-2002 : Added methods for image maps (DG); 42: * 05-Aug-2002 : Added constructor and accessors for URL support in image maps 43: * Added getImageMapAreaTag() - previously in subclasses (RA); 44: * 05-Sep-2002 : Added getImageMapAreaTag(boolean) to support OverLIB for 45: * tooltips http://www.bosrup.com/web/overlib (RA); 46: * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG); 47: * 08-Oct-2002 : Changed getImageMapAreaTag to use title instead of alt 48: * attribute so HTML image maps now work in Mozilla and Opera as 49: * well as Internet Explorer (RA); 50: * 13-Mar-2003 : Change getImageMapAreaTag to only return a tag when there is a 51: * tooltip or URL, as suggested by Xavier Poinsard (see Feature 52: * Request 688079) (DG); 53: * 12-Aug-2003 : Added support for custom image maps using 54: * ToolTipTagFragmentGenerator and URLTagFragmentGenerator (RA); 55: * 02-Sep-2003 : Incorporated fix (791901) submitted by Robert Fuller (DG); 56: * 19-May-2004 : Added equals() method and implemented Cloneable and 57: * Serializable (DG); 58: * 29-Sep-2004 : Implemented PublicCloneable (DG); 59: * 13-Jan-2005 : Fixed for compliance with XHTML 1.0 (DG); 60: * 18-Apr-2005 : Use StringBuffer (DG); 61: * 20-Apr-2005 : Added toString() implementation (DG); 62: * ------------- JFREECHART 1.0.x --------------------------------------------- 63: * 06-Feb-2007 : API doc update (DG); 64: * 13-Nov-2007 : Reorganised equals(), implemented hashCode (DG); 65: * 04-Dec-2007 : Added 'nohref' attribute in getImageMapAreaTag() method, to 66: * fix bug 1460195 (DG); 67: * 04-Dec-2007 : Escape the toolTipText and urlText in getImageMapAreaTag() to 68: * prevent special characters corrupting the HTML (DG); 69: * 05-Dec-2007 : Previous change reverted - let the tool tip and url tag 70: * generators handle filtering / escaping (DG); 71: * 72: */ 73: 74: package org.jfree.chart.entity; 75: 76: import java.awt.Shape; 77: import java.awt.geom.PathIterator; 78: import java.awt.geom.Rectangle2D; 79: import java.io.IOException; 80: import java.io.ObjectInputStream; 81: import java.io.ObjectOutputStream; 82: import java.io.Serializable; 83: 84: import org.jfree.chart.HashUtilities; 85: import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator; 86: import org.jfree.chart.imagemap.URLTagFragmentGenerator; 87: import org.jfree.io.SerialUtilities; 88: import org.jfree.util.ObjectUtilities; 89: import org.jfree.util.PublicCloneable; 90: 91: /** 92: * A class that captures information about some component of a chart (a bar, 93: * line etc). 94: */ 95: public class ChartEntity implements Cloneable, PublicCloneable, Serializable { 96: 97: /** For serialization. */ 98: private static final long serialVersionUID = -4445994133561919083L; 99: 100: /** The area occupied by the entity (in Java 2D space). */ 101: private transient Shape area; 102: 103: /** The tool tip text for the entity. */ 104: private String toolTipText; 105: 106: /** The URL text for the entity. */ 107: private String urlText; 108: 109: /** 110: * Creates a new chart entity. 111: * 112: * @param area the area (<code>null</code> not permitted). 113: */ 114: public ChartEntity(Shape area) { 115: // defer argument checks... 116: this(area, null); 117: } 118: 119: /** 120: * Creates a new chart entity. 121: * 122: * @param area the area (<code>null</code> not permitted). 123: * @param toolTipText the tool tip text (<code>null</code> permitted). 124: */ 125: public ChartEntity(Shape area, String toolTipText) { 126: // defer argument checks... 127: this(area, toolTipText, null); 128: } 129: 130: /** 131: * Creates a new entity. 132: * 133: * @param area the area (<code>null</code> not permitted). 134: * @param toolTipText the tool tip text (<code>null</code> permitted). 135: * @param urlText the URL text for HTML image maps (<code>null</code> 136: * permitted). 137: */ 138: public ChartEntity(Shape area, String toolTipText, String urlText) { 139: if (area == null) { 140: throw new IllegalArgumentException("Null 'area' argument."); 141: } 142: this.area = area; 143: this.toolTipText = toolTipText; 144: this.urlText = urlText; 145: } 146: 147: /** 148: * Returns the area occupied by the entity (in Java 2D space). 149: * 150: * @return The area (never <code>null</code>). 151: */ 152: public Shape getArea() { 153: return this.area; 154: } 155: 156: /** 157: * Sets the area for the entity. 158: * <P> 159: * This class conveys information about chart entities back to a client. 160: * Setting this area doesn't change the entity (which has already been 161: * drawn). 162: * 163: * @param area the area (<code>null</code> not permitted). 164: */ 165: public void setArea(Shape area) { 166: if (area == null) { 167: throw new IllegalArgumentException("Null 'area' argument."); 168: } 169: this.area = area; 170: } 171: 172: /** 173: * Returns the tool tip text for the entity. Be aware that this text 174: * may have been generated from user supplied data, so for security 175: * reasons some form of filtering should be applied before incorporating 176: * this text into any HTML output. 177: * 178: * @return The tool tip text (possibly <code>null</code>). 179: */ 180: public String getToolTipText() { 181: return this.toolTipText; 182: } 183: 184: /** 185: * Sets the tool tip text. 186: * 187: * @param text the text (<code>null</code> permitted). 188: */ 189: public void setToolTipText(String text) { 190: this.toolTipText = text; 191: } 192: 193: /** 194: * Returns the URL text for the entity. Be aware that this text 195: * may have been generated from user supplied data, so some form of 196: * filtering should be applied before this "URL" is used in any output. 197: * 198: * @return The URL text (possibly <code>null</code>). 199: */ 200: public String getURLText() { 201: return this.urlText; 202: } 203: 204: /** 205: * Sets the URL text. 206: * 207: * @param text the text (<code>null</code> permitted). 208: */ 209: public void setURLText(String text) { 210: this.urlText = text; 211: } 212: 213: /** 214: * Returns a string describing the entity area. This string is intended 215: * for use in an AREA tag when generating an image map. 216: * 217: * @return The shape type (never <code>null</code>). 218: */ 219: public String getShapeType() { 220: if (this.area instanceof Rectangle2D) { 221: return "rect"; 222: } 223: else { 224: return "poly"; 225: } 226: } 227: 228: /** 229: * Returns the shape coordinates as a string. 230: * 231: * @return The shape coordinates (never <code>null</code>). 232: */ 233: public String getShapeCoords() { 234: if (this.area instanceof Rectangle2D) { 235: return getRectCoords((Rectangle2D) this.area); 236: } 237: else { 238: return getPolyCoords(this.area); 239: } 240: } 241: 242: /** 243: * Returns a string containing the coordinates (x1, y1, x2, y2) for a given 244: * rectangle. This string is intended for use in an image map. 245: * 246: * @param rectangle the rectangle (<code>null</code> not permitted). 247: * 248: * @return Upper left and lower right corner of a rectangle. 249: */ 250: private String getRectCoords(Rectangle2D rectangle) { 251: if (rectangle == null) { 252: throw new IllegalArgumentException("Null 'rectangle' argument."); 253: } 254: int x1 = (int) rectangle.getX(); 255: int y1 = (int) rectangle.getY(); 256: int x2 = x1 + (int) rectangle.getWidth(); 257: int y2 = y1 + (int) rectangle.getHeight(); 258: // fix by rfuller 259: if (x2 == x1) { 260: x2++; 261: } 262: if (y2 == y1) { 263: y2++; 264: } 265: // end fix by rfuller 266: return x1 + "," + y1 + "," + x2 + "," + y2; 267: } 268: 269: /** 270: * Returns a string containing the coordinates for a given shape. This 271: * string is intended for use in an image map. 272: * 273: * @param shape the shape (<code>null</code> not permitted). 274: * 275: * @return The coordinates for a given shape as string. 276: */ 277: private String getPolyCoords(Shape shape) { 278: if (shape == null) { 279: throw new IllegalArgumentException("Null 'shape' argument."); 280: } 281: StringBuffer result = new StringBuffer(); 282: boolean first = true; 283: float[] coords = new float[6]; 284: PathIterator pi = shape.getPathIterator(null, 1.0); 285: while (!pi.isDone()) { 286: pi.currentSegment(coords); 287: if (first) { 288: first = false; 289: result.append((int) coords[0]); 290: result.append(",").append((int) coords[1]); 291: } 292: else { 293: result.append(","); 294: result.append((int) coords[0]); 295: result.append(","); 296: result.append((int) coords[1]); 297: } 298: pi.next(); 299: } 300: return result.toString(); 301: } 302: 303: /** 304: * Returns an HTML image map tag for this entity. The returned fragment 305: * should be <code>XHTML 1.0</code> compliant. 306: * 307: * @param toolTipTagFragmentGenerator a generator for the HTML fragment 308: * that will contain the tooltip text (<code>null</code> not permitted 309: * if this entity contains tooltip information). 310: * @param urlTagFragmentGenerator a generator for the HTML fragment that 311: * will contain the URL reference (<code>null</code> not permitted if 312: * this entity has a URL). 313: * 314: * @return The HTML tag. 315: */ 316: public String getImageMapAreaTag( 317: ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, 318: URLTagFragmentGenerator urlTagFragmentGenerator) { 319: 320: StringBuffer tag = new StringBuffer(); 321: boolean hasURL = (this.urlText == null ? false 322: : !this.urlText.equals("")); 323: boolean hasToolTip = (this.toolTipText == null ? false 324: : !this.toolTipText.equals("")); 325: if (hasURL || hasToolTip) { 326: tag.append("<area shape=\"" + getShapeType() + "\"" + " coords=\"" 327: + getShapeCoords() + "\""); 328: if (hasToolTip) { 329: tag.append(toolTipTagFragmentGenerator.generateToolTipFragment( 330: this.toolTipText)); 331: } 332: if (hasURL) { 333: tag.append(urlTagFragmentGenerator.generateURLFragment( 334: this.urlText)); 335: } 336: else { 337: tag.append(" nohref=\"nohref\""); 338: } 339: // if there is a tool tip, we expect it to generate the title and 340: // alt values, so we only add an empty alt if there is no tooltip 341: if (!hasToolTip) { 342: tag.append(" alt=\"\""); 343: } 344: tag.append("/>"); 345: } 346: return tag.toString(); 347: } 348: 349: /** 350: * Returns a string representation of the chart entity, useful for 351: * debugging. 352: * 353: * @return A string. 354: */ 355: public String toString() { 356: StringBuffer buf = new StringBuffer("ChartEntity: "); 357: buf.append("tooltip = "); 358: buf.append(this.toolTipText); 359: return buf.toString(); 360: } 361: 362: /** 363: * Tests the entity for equality with an arbitrary object. 364: * 365: * @param obj the object to test against (<code>null</code> permitted). 366: * 367: * @return A boolean. 368: */ 369: public boolean equals(Object obj) { 370: if (obj == this) { 371: return true; 372: } 373: if (!(obj instanceof ChartEntity)) { 374: return false; 375: } 376: ChartEntity that = (ChartEntity) obj; 377: if (!this.area.equals(that.area)) { 378: return false; 379: } 380: if (!ObjectUtilities.equal(this.toolTipText, that.toolTipText)) { 381: return false; 382: } 383: if (!ObjectUtilities.equal(this.urlText, that.urlText)) { 384: return false; 385: } 386: return true; 387: } 388: 389: /** 390: * Returns a hash code for this instance. 391: * 392: * @return A hash code. 393: */ 394: public int hashCode() { 395: int result = 37; 396: result = HashUtilities.hashCode(result, this.toolTipText); 397: result = HashUtilities.hashCode(result, this.urlText); 398: return result; 399: } 400: 401: /** 402: * Returns a clone of the entity. 403: * 404: * @return A clone. 405: * 406: * @throws CloneNotSupportedException if there is a problem cloning the 407: * entity. 408: */ 409: public Object clone() throws CloneNotSupportedException { 410: return super.clone(); 411: } 412: 413: /** 414: * Provides serialization support. 415: * 416: * @param stream the output stream. 417: * 418: * @throws IOException if there is an I/O error. 419: */ 420: private void writeObject(ObjectOutputStream stream) throws IOException { 421: stream.defaultWriteObject(); 422: SerialUtilities.writeShape(this.area, stream); 423: } 424: 425: /** 426: * Provides serialization support. 427: * 428: * @param stream the input stream. 429: * 430: * @throws IOException if there is an I/O error. 431: * @throws ClassNotFoundException if there is a classpath problem. 432: */ 433: private void readObject(ObjectInputStream stream) 434: throws IOException, ClassNotFoundException { 435: stream.defaultReadObject(); 436: this.area = SerialUtilities.readShape(stream); 437: } 438: 439: }