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 * Changes:
038 * --------
039 * 23-May-2002 : Version 1 (DG);
040 * 12-Jun-2002 : Added Javadoc comments (DG);
041 * 26-Jun-2002 : Added methods for image maps (DG);
042 * 05-Aug-2002 : Added constructor and accessors for URL support in image maps
043 * Added getImageMapAreaTag() - previously in subclasses (RA);
044 * 05-Sep-2002 : Added getImageMapAreaTag(boolean) to support OverLIB for
045 * tooltips http://www.bosrup.com/web/overlib (RA);
046 * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
047 * 08-Oct-2002 : Changed getImageMapAreaTag to use title instead of alt
048 * attribute so HTML image maps now work in Mozilla and Opera as
049 * well as Internet Explorer (RA);
050 * 13-Mar-2003 : Change getImageMapAreaTag to only return a tag when there is a
051 * tooltip or URL, as suggested by Xavier Poinsard (see Feature
052 * Request 688079) (DG);
053 * 12-Aug-2003 : Added support for custom image maps using
054 * ToolTipTagFragmentGenerator and URLTagFragmentGenerator (RA);
055 * 02-Sep-2003 : Incorporated fix (791901) submitted by Robert Fuller (DG);
056 * 19-May-2004 : Added equals() method and implemented Cloneable and
057 * Serializable (DG);
058 * 29-Sep-2004 : Implemented PublicCloneable (DG);
059 * 13-Jan-2005 : Fixed for compliance with XHTML 1.0 (DG);
060 * 18-Apr-2005 : Use StringBuffer (DG);
061 * 20-Apr-2005 : Added toString() implementation (DG);
062 * ------------- JFREECHART 1.0.x ---------------------------------------------
063 * 06-Feb-2007 : API doc update (DG);
064 * 13-Nov-2007 : Reorganised equals(), implemented hashCode (DG);
065 *
066 */
067
068 package org.jfree.chart.entity;
069
070 import java.awt.Shape;
071 import java.awt.geom.PathIterator;
072 import java.awt.geom.Rectangle2D;
073 import java.io.IOException;
074 import java.io.ObjectInputStream;
075 import java.io.ObjectOutputStream;
076 import java.io.Serializable;
077
078 import org.jfree.chart.HashUtilities;
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. Be aware that this text
168 * may have been generated from user supplied data, so for security
169 * reasons some form of filtering should be applied before incorporating
170 * this text into any HTML output.
171 *
172 * @return The tool tip text (possibly <code>null</code>).
173 */
174 public String getToolTipText() {
175 return this.toolTipText;
176 }
177
178 /**
179 * Sets the tool tip text.
180 *
181 * @param text the text (<code>null</code> permitted).
182 */
183 public void setToolTipText(String text) {
184 this.toolTipText = text;
185 }
186
187 /**
188 * Returns the URL text for the entity. Be aware that this text
189 * may have been generated from user supplied data, so some form of
190 * filtering should be applied before this "URL" is used in any output.
191 *
192 * @return The URL text (possibly <code>null</code>).
193 */
194 public String getURLText() {
195 return this.urlText;
196 }
197
198 /**
199 * Sets the URL text.
200 *
201 * @param text the text (<code>null</code> permitted).
202 */
203 public void setURLText(String text) {
204 this.urlText = text;
205 }
206
207 /**
208 * Returns a string describing the entity area. This string is intended
209 * for use in an AREA tag when generating an image map.
210 *
211 * @return The shape type (never <code>null</code>).
212 */
213 public String getShapeType() {
214 if (this.area instanceof Rectangle2D) {
215 return "rect";
216 }
217 else {
218 return "poly";
219 }
220 }
221
222 /**
223 * Returns the shape coordinates as a string.
224 *
225 * @return The shape coordinates (never <code>null</code>).
226 */
227 public String getShapeCoords() {
228 if (this.area instanceof Rectangle2D) {
229 return getRectCoords((Rectangle2D) this.area);
230 }
231 else {
232 return getPolyCoords(this.area);
233 }
234 }
235
236 /**
237 * Returns a string containing the coordinates (x1, y1, x2, y2) for a given
238 * rectangle. This string is intended for use in an image map.
239 *
240 * @param rectangle the rectangle (<code>null</code> not permitted).
241 *
242 * @return Upper left and lower right corner of a rectangle.
243 */
244 private String getRectCoords(Rectangle2D rectangle) {
245 if (rectangle == null) {
246 throw new IllegalArgumentException("Null 'rectangle' argument.");
247 }
248 int x1 = (int) rectangle.getX();
249 int y1 = (int) rectangle.getY();
250 int x2 = x1 + (int) rectangle.getWidth();
251 int y2 = y1 + (int) rectangle.getHeight();
252 // fix by rfuller
253 if (x2 == x1) {
254 x2++;
255 }
256 if (y2 == y1) {
257 y2++;
258 }
259 // end fix by rfuller
260 return x1 + "," + y1 + "," + x2 + "," + y2;
261 }
262
263 /**
264 * Returns a string containing the coordinates for a given shape. This
265 * string is intended for use in an image map.
266 *
267 * @param shape the shape (<code>null</code> not permitted).
268 *
269 * @return The coordinates for a given shape as string.
270 */
271 private String getPolyCoords(Shape shape) {
272 if (shape == null) {
273 throw new IllegalArgumentException("Null 'shape' argument.");
274 }
275 StringBuffer result = new StringBuffer();
276 boolean first = true;
277 float[] coords = new float[6];
278 PathIterator pi = shape.getPathIterator(null, 1.0);
279 while (!pi.isDone()) {
280 pi.currentSegment(coords);
281 if (first) {
282 first = false;
283 result.append((int) coords[0]);
284 result.append(",").append((int) coords[1]);
285 }
286 else {
287 result.append(",");
288 result.append((int) coords[0]);
289 result.append(",");
290 result.append((int) coords[1]);
291 }
292 pi.next();
293 }
294 return result.toString();
295 }
296
297 /**
298 * Returns an HTML image map tag for this entity. The returned fragment
299 * should be <code>XHTML 1.0</code> compliant.
300 *
301 * @param toolTipTagFragmentGenerator a generator for the HTML fragment
302 * that will contain the tooltip text (<code>null</code> not permitted
303 * if this entity contains tooltip information).
304 * @param urlTagFragmentGenerator a generator for the HTML fragment that
305 * will contain the URL reference (<code>null</code> not permitted if
306 * this entity has a URL).
307 *
308 * @return The HTML tag.
309 */
310 public String getImageMapAreaTag(
311 ToolTipTagFragmentGenerator toolTipTagFragmentGenerator,
312 URLTagFragmentGenerator urlTagFragmentGenerator) {
313
314 StringBuffer tag = new StringBuffer();
315 boolean hasURL = (this.urlText == null ? false
316 : !this.urlText.equals(""));
317 boolean hasToolTip = (this.toolTipText == null ? false
318 : !this.toolTipText.equals(""));
319 if (hasURL || hasToolTip) {
320 tag.append("<area shape=\"" + getShapeType() + "\"" + " coords=\""
321 + getShapeCoords() + "\"");
322 if (hasToolTip) {
323 tag.append(toolTipTagFragmentGenerator.generateToolTipFragment(
324 this.toolTipText));
325 }
326 if (hasURL) {
327 tag.append(urlTagFragmentGenerator.generateURLFragment(
328 this.urlText));
329 }
330 // if there is a tool tip, we expect it to generate the title and
331 // alt values, so we only add an empty alt if there is no tooltip
332 if (!hasToolTip) {
333 tag.append(" alt=\"\"");
334 }
335 tag.append("/>");
336 }
337 return tag.toString();
338 }
339
340 /**
341 * Returns a string representation of the chart entity, useful for
342 * debugging.
343 *
344 * @return A string.
345 */
346 public String toString() {
347 StringBuffer buf = new StringBuffer("ChartEntity: ");
348 buf.append("tooltip = ");
349 buf.append(this.toolTipText);
350 return buf.toString();
351 }
352
353 /**
354 * Tests the entity for equality with an arbitrary object.
355 *
356 * @param obj the object to test against (<code>null</code> permitted).
357 *
358 * @return A boolean.
359 */
360 public boolean equals(Object obj) {
361 if (obj == this) {
362 return true;
363 }
364 if (!(obj instanceof ChartEntity)) {
365 return false;
366 }
367 ChartEntity that = (ChartEntity) obj;
368 if (!this.area.equals(that.area)) {
369 return false;
370 }
371 if (!ObjectUtilities.equal(this.toolTipText, that.toolTipText)) {
372 return false;
373 }
374 if (!ObjectUtilities.equal(this.urlText, that.urlText)) {
375 return false;
376 }
377 return true;
378 }
379
380 /**
381 * Returns a hash code for this instance.
382 *
383 * @return A hash code.
384 */
385 public int hashCode() {
386 int result = 37;
387 result = HashUtilities.hashCode(result, this.toolTipText);
388 result = HashUtilities.hashCode(result, this.urlText);
389 return result;
390 }
391
392 /**
393 * Returns a clone of the entity.
394 *
395 * @return A clone.
396 *
397 * @throws CloneNotSupportedException if there is a problem cloning the
398 * entity.
399 */
400 public Object clone() throws CloneNotSupportedException {
401 return super.clone();
402 }
403
404 /**
405 * Provides serialization support.
406 *
407 * @param stream the output stream.
408 *
409 * @throws IOException if there is an I/O error.
410 */
411 private void writeObject(ObjectOutputStream stream) throws IOException {
412 stream.defaultWriteObject();
413 SerialUtilities.writeShape(this.area, stream);
414 }
415
416 /**
417 * Provides serialization support.
418 *
419 * @param stream the input stream.
420 *
421 * @throws IOException if there is an I/O error.
422 * @throws ClassNotFoundException if there is a classpath problem.
423 */
424 private void readObject(ObjectInputStream stream)
425 throws IOException, ClassNotFoundException {
426 stream.defaultReadObject();
427 this.area = SerialUtilities.readShape(stream);
428 }
429
430 }