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-2005, 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-2005, 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.1 2005/10/25 20:41:59 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:  *
  65:  */
  66: 
  67: package org.jfree.chart.entity;
  68: 
  69: import java.awt.Shape;
  70: import java.awt.geom.PathIterator;
  71: import java.awt.geom.Rectangle2D;
  72: import java.io.IOException;
  73: import java.io.ObjectInputStream;
  74: import java.io.ObjectOutputStream;
  75: import java.io.Serializable;
  76: 
  77: import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator;
  78: import org.jfree.chart.imagemap.URLTagFragmentGenerator;
  79: import org.jfree.io.SerialUtilities;
  80: import org.jfree.util.ObjectUtilities;
  81: import org.jfree.util.PublicCloneable;
  82: 
  83: /**
  84:  * A class that captures information about some component of a chart (a bar, 
  85:  * line etc).
  86:  */
  87: public class ChartEntity implements Cloneable, PublicCloneable, Serializable {
  88: 
  89:     /** For serialization. */
  90:     private static final long serialVersionUID = -4445994133561919083L;
  91:     
  92:     /** The area occupied by the entity (in Java 2D space). */
  93:     private transient Shape area;
  94: 
  95:     /** The tool tip text for the entity. */
  96:     private String toolTipText;
  97: 
  98:     /** The URL text for the entity. */
  99:     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: }