Source for org.jfree.chart.entity.ChartEntity

   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:  * $Id: ChartEntity.java,v 1.8.2.2 2007/02/06 11:28:53 mungady Exp $
  38:  *
  39:  * Changes:
  40:  * --------
  41:  * 23-May-2002 : Version 1 (DG);
  42:  * 12-Jun-2002 : Added Javadoc comments (DG);
  43:  * 26-Jun-2002 : Added methods for image maps (DG);
  44:  * 05-Aug-2002 : Added constructor and accessors for URL support in image maps
  45:  *               Added getImageMapAreaTag() - previously in subclasses (RA);
  46:  * 05-Sep-2002 : Added getImageMapAreaTag(boolean) to support OverLIB for 
  47:  *               tooltips http://www.bosrup.com/web/overlib (RA);
  48:  * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  49:  * 08-Oct-2002 : Changed getImageMapAreaTag to use title instead of alt 
  50:  *               attribute so HTML image maps now work in Mozilla and Opera as 
  51:  *               well as Internet Explorer (RA);
  52:  * 13-Mar-2003 : Change getImageMapAreaTag to only return a tag when there is a
  53:  *               tooltip or URL, as suggested by Xavier Poinsard (see Feature 
  54:  *               Request 688079) (DG);
  55:  * 12-Aug-2003 : Added support for custom image maps using 
  56:  *               ToolTipTagFragmentGenerator and URLTagFragmentGenerator (RA);
  57:  * 02-Sep-2003 : Incorporated fix (791901) submitted by Robert Fuller (DG);
  58:  * 19-May-2004 : Added equals() method and implemented Cloneable and 
  59:  *               Serializable (DG);
  60:  * 29-Sep-2004 : Implemented PublicCloneable (DG);
  61:  * 13-Jan-2005 : Fixed for compliance with XHTML 1.0 (DG);
  62:  * 18-Apr-2005 : Use StringBuffer (DG);
  63:  * 20-Apr-2005 : Added toString() implementation (DG);
  64:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  65:  * 06-Feb-2007 : API doc update (DG);
  66:  *
  67:  */
  68: 
  69: package org.jfree.chart.entity;
  70: 
  71: import java.awt.Shape;
  72: import java.awt.geom.PathIterator;
  73: import java.awt.geom.Rectangle2D;
  74: import java.io.IOException;
  75: import java.io.ObjectInputStream;
  76: import java.io.ObjectOutputStream;
  77: import java.io.Serializable;
  78: 
  79: import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator;
  80: import org.jfree.chart.imagemap.URLTagFragmentGenerator;
  81: import org.jfree.io.SerialUtilities;
  82: import org.jfree.util.ObjectUtilities;
  83: import org.jfree.util.PublicCloneable;
  84: 
  85: /**
  86:  * A class that captures information about some component of a chart (a bar, 
  87:  * line etc).
  88:  */
  89: public class ChartEntity implements Cloneable, PublicCloneable, Serializable {
  90: 
  91:     /** For serialization. */
  92:     private static final long serialVersionUID = -4445994133561919083L;
  93:     
  94:     /** The area occupied by the entity (in Java 2D space). */
  95:     private transient Shape area;
  96: 
  97:     /** The tool tip text for the entity. */
  98:     private String toolTipText;
  99: 
 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: }