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 }