001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2006, 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 * Marker.java 029 * ----------- 030 * (C) Copyright 2002-2006, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Nicolas Brodu; 034 * 035 * $Id: Marker.java,v 1.10.2.5 2006/09/05 14:34:23 mungady Exp $ 036 * 037 * Changes (since 2-Jul-2002) 038 * -------------------------- 039 * 02-Jul-2002 : Added extra constructor, standard header and Javadoc 040 * comments (DG); 041 * 20-Aug-2002 : Added the outline stroke attribute (DG); 042 * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG); 043 * 16-Oct-2002 : Added new constructor (DG); 044 * 26-Mar-2003 : Implemented Serializable (DG); 045 * 21-May-2003 : Added labels (DG); 046 * 11-Sep-2003 : Implemented Cloneable (NB); 047 * 05-Nov-2003 : Added checks to ensure some attributes are never null (DG); 048 * 11-Feb-2003 : Moved to org.jfree.chart.plot package, plus significant API 049 * changes to support IntervalMarker in plots (DG); 050 * 14-Jun-2004 : Updated equals() method (DG); 051 * 21-Jan-2005 : Added settings to control direction of horizontal and 052 * vertical label offsets (DG); 053 * 01-Jun-2005 : Modified to use only one label offset type - this will be 054 * applied to the domain or range axis as appropriate (DG); 055 * 06-Jun-2005 : Fix equals() method to handle GradientPaint (DG); 056 * 19-Aug-2005 : Changed constructor from public --> protected (DG); 057 * ------------- JFREECHART 1.0.0 --------------------------------------------- 058 * 05-Sep-2006 : Added MarkerChangeListener support (DG); 059 * 060 */ 061 062 package org.jfree.chart.plot; 063 064 import java.awt.BasicStroke; 065 import java.awt.Color; 066 import java.awt.Font; 067 import java.awt.Paint; 068 import java.awt.Stroke; 069 import java.io.IOException; 070 import java.io.ObjectInputStream; 071 import java.io.ObjectOutputStream; 072 import java.io.Serializable; 073 import java.util.EventListener; 074 075 import javax.swing.event.EventListenerList; 076 077 import org.jfree.chart.event.MarkerChangeEvent; 078 import org.jfree.chart.event.MarkerChangeListener; 079 import org.jfree.io.SerialUtilities; 080 import org.jfree.ui.LengthAdjustmentType; 081 import org.jfree.ui.RectangleAnchor; 082 import org.jfree.ui.RectangleInsets; 083 import org.jfree.ui.TextAnchor; 084 import org.jfree.util.ObjectUtilities; 085 import org.jfree.util.PaintUtilities; 086 087 /** 088 * The base class for markers that can be added to plots to highlight a value 089 * or range of values. 090 * <br><br> 091 * An event notification mechanism was added to this class in JFreeChart 092 * version 1.0.3. 093 */ 094 public abstract class Marker implements Cloneable, Serializable { 095 096 /** For serialization. */ 097 private static final long serialVersionUID = -734389651405327166L; 098 099 /** The paint. */ 100 private transient Paint paint; 101 102 /** The stroke. */ 103 private transient Stroke stroke; 104 105 /** The outline paint. */ 106 private transient Paint outlinePaint; 107 108 /** The outline stroke. */ 109 private transient Stroke outlineStroke; 110 111 /** The alpha transparency. */ 112 private float alpha; 113 114 /** The label. */ 115 private String label = null; 116 117 /** The label font. */ 118 private Font labelFont; 119 120 /** The label paint. */ 121 private transient Paint labelPaint; 122 123 /** The label position. */ 124 private RectangleAnchor labelAnchor; 125 126 /** The text anchor for the label. */ 127 private TextAnchor labelTextAnchor; 128 129 /** The label offset from the marker rectangle. */ 130 private RectangleInsets labelOffset; 131 132 /** 133 * The offset type for the domain or range axis (never <code>null</code>). 134 */ 135 private LengthAdjustmentType labelOffsetType; 136 137 /** Storage for registered change listeners. */ 138 private transient EventListenerList listenerList; 139 140 /** 141 * Creates a new marker with default attributes. 142 */ 143 protected Marker() { 144 this(Color.gray); 145 } 146 147 /** 148 * Constructs a new marker. 149 * 150 * @param paint the paint (<code>null</code> not permitted). 151 */ 152 protected Marker(Paint paint) { 153 this(paint, new BasicStroke(0.5f), Color.gray, new BasicStroke(0.5f), 154 0.80f); 155 } 156 157 /** 158 * Constructs a new marker. 159 * 160 * @param paint the paint (<code>null</code> not permitted). 161 * @param stroke the stroke (<code>null</code> not permitted). 162 * @param outlinePaint the outline paint (<code>null</code> permitted). 163 * @param outlineStroke the outline stroke (<code>null</code> permitted). 164 * @param alpha the alpha transparency (must be in the range 0.0f to 165 * 1.0f). 166 * 167 * @throws IllegalArgumentException if <code>paint</code> or 168 * <code>stroke</code> is <code>null</code>, or <code>alpha</code> is 169 * not in the specified range. 170 */ 171 protected Marker(Paint paint, Stroke stroke, 172 Paint outlinePaint, Stroke outlineStroke, 173 float alpha) { 174 175 if (paint == null) { 176 throw new IllegalArgumentException("Null 'paint' argument."); 177 } 178 if (stroke == null) { 179 throw new IllegalArgumentException("Null 'stroke' argument."); 180 } 181 if (alpha < 0.0f || alpha > 1.0f) 182 throw new IllegalArgumentException( 183 "The 'alpha' value must be in the range 0.0f to 1.0f"); 184 185 this.paint = paint; 186 this.stroke = stroke; 187 this.outlinePaint = outlinePaint; 188 this.outlineStroke = outlineStroke; 189 this.alpha = alpha; 190 191 this.labelFont = new Font("SansSerif", Font.PLAIN, 9); 192 this.labelPaint = Color.black; 193 this.labelAnchor = RectangleAnchor.TOP_LEFT; 194 this.labelOffset = new RectangleInsets(3.0, 3.0, 3.0, 3.0); 195 this.labelOffsetType = LengthAdjustmentType.CONTRACT; 196 this.labelTextAnchor = TextAnchor.CENTER; 197 198 this.listenerList = new EventListenerList(); 199 } 200 201 /** 202 * Returns the paint. 203 * 204 * @return The paint (never <code>null</code>). 205 * 206 * @see #setPaint(Paint) 207 */ 208 public Paint getPaint() { 209 return this.paint; 210 } 211 212 /** 213 * Sets the paint and sends a {@link MarkerChangeEvent} to all registered 214 * listeners. 215 * 216 * @param paint the paint (<code>null</code> not permitted). 217 * 218 * @see #getPaint() 219 */ 220 public void setPaint(Paint paint) { 221 if (paint == null) { 222 throw new IllegalArgumentException("Null 'paint' argument."); 223 } 224 this.paint = paint; 225 notifyListeners(new MarkerChangeEvent(this)); 226 } 227 228 /** 229 * Returns the stroke. 230 * 231 * @return The stroke (never <code>null</code>). 232 * 233 * @see #setStroke(Stroke) 234 */ 235 public Stroke getStroke() { 236 return this.stroke; 237 } 238 239 /** 240 * Sets the stroke and sends a {@link MarkerChangeEvent} to all registered 241 * listeners. 242 * 243 * @param stroke the stroke (<code>null</code> not permitted). 244 * 245 * @see #getStroke() 246 */ 247 public void setStroke(Stroke stroke) { 248 if (stroke == null) { 249 throw new IllegalArgumentException("Null 'stroke' argument."); 250 } 251 this.stroke = stroke; 252 notifyListeners(new MarkerChangeEvent(this)); 253 } 254 255 /** 256 * Returns the outline paint. 257 * 258 * @return The outline paint (possibly <code>null</code>). 259 * 260 * @see #setOutlinePaint(Paint) 261 */ 262 public Paint getOutlinePaint() { 263 return this.outlinePaint; 264 } 265 266 /** 267 * Sets the outline paint and sends a {@link MarkerChangeEvent} to all 268 * registered listeners. 269 * 270 * @param paint the paint (<code>null</code> permitted). 271 * 272 * @see #getOutlinePaint() 273 */ 274 public void setOutlinePaint(Paint paint) { 275 this.outlinePaint = paint; 276 notifyListeners(new MarkerChangeEvent(this)); 277 } 278 279 /** 280 * Returns the outline stroke. 281 * 282 * @return The outline stroke (possibly <code>null</code>). 283 * 284 * @see #setOutlineStroke(Stroke) 285 */ 286 public Stroke getOutlineStroke() { 287 return this.outlineStroke; 288 } 289 290 /** 291 * Sets the outline stroke and sends a {@link MarkerChangeEvent} to all 292 * registered listeners. 293 * 294 * @param stroke the stroke (<code>null</code> permitted). 295 * 296 * @see #getOutlineStroke() 297 */ 298 public void setOutlineStroke(Stroke stroke) { 299 this.outlineStroke = stroke; 300 notifyListeners(new MarkerChangeEvent(this)); 301 } 302 303 /** 304 * Returns the alpha transparency. 305 * 306 * @return The alpha transparency. 307 * 308 * @see #setAlpha(float) 309 */ 310 public float getAlpha() { 311 return this.alpha; 312 } 313 314 /** 315 * Sets the alpha transparency that should be used when drawing the 316 * marker, and sends a {@link MarkerChangeEvent} to all registered 317 * listeners. The alpha transparency is a value in the range 0.0f 318 * (completely transparent) to 1.0f (completely opaque). 319 * 320 * @param alpha the alpha transparency (must be in the range 0.0f to 321 * 1.0f). 322 * 323 * @throws IllegalArgumentException if <code>alpha</code> is not in the 324 * specified range. 325 * 326 * @see #getAlpha() 327 */ 328 public void setAlpha(float alpha) { 329 if (alpha < 0.0f || alpha > 1.0f) 330 throw new IllegalArgumentException( 331 "The 'alpha' value must be in the range 0.0f to 1.0f"); 332 this.alpha = alpha; 333 notifyListeners(new MarkerChangeEvent(this)); 334 } 335 336 /** 337 * Returns the label (if <code>null</code> no label is displayed). 338 * 339 * @return The label (possibly <code>null</code>). 340 * 341 * @see #setLabel(String) 342 */ 343 public String getLabel() { 344 return this.label; 345 } 346 347 /** 348 * Sets the label (if <code>null</code> no label is displayed) and sends a 349 * {@link MarkerChangeEvent} to all registered listeners. 350 * 351 * @param label the label (<code>null</code> permitted). 352 * 353 * @see #getLabel() 354 */ 355 public void setLabel(String label) { 356 this.label = label; 357 notifyListeners(new MarkerChangeEvent(this)); 358 } 359 360 /** 361 * Returns the label font. 362 * 363 * @return The label font (never <code>null</code>). 364 * 365 * @see #setLabelFont(Font) 366 */ 367 public Font getLabelFont() { 368 return this.labelFont; 369 } 370 371 /** 372 * Sets the label font and sends a {@link MarkerChangeEvent} to all 373 * registered listeners. 374 * 375 * @param font the font (<code>null</code> not permitted). 376 * 377 * @see #getLabelFont() 378 */ 379 public void setLabelFont(Font font) { 380 if (font == null) { 381 throw new IllegalArgumentException("Null 'font' argument."); 382 } 383 this.labelFont = font; 384 notifyListeners(new MarkerChangeEvent(this)); 385 } 386 387 /** 388 * Returns the label paint. 389 * 390 * @return The label paint (never </code>null</code>). 391 * 392 * @see #setLabelPaint(Paint) 393 */ 394 public Paint getLabelPaint() { 395 return this.labelPaint; 396 } 397 398 /** 399 * Sets the label paint and sends a {@link MarkerChangeEvent} to all 400 * registered listeners. 401 * 402 * @param paint the paint (<code>null</code> not permitted). 403 * 404 * @see #getLabelPaint() 405 */ 406 public void setLabelPaint(Paint paint) { 407 if (paint == null) { 408 throw new IllegalArgumentException("Null 'paint' argument."); 409 } 410 this.labelPaint = paint; 411 notifyListeners(new MarkerChangeEvent(this)); 412 } 413 414 /** 415 * Returns the label anchor. This defines the position of the label 416 * anchor, relative to the bounds of the marker. 417 * 418 * @return The label anchor (never <code>null</code>). 419 * 420 * @see #setLabelAnchor(RectangleAnchor) 421 */ 422 public RectangleAnchor getLabelAnchor() { 423 return this.labelAnchor; 424 } 425 426 /** 427 * Sets the label anchor and sends a {@link MarkerChangeEvent} to all 428 * registered listeners. The anchor defines the position of the label 429 * anchor, relative to the bounds of the marker. 430 * 431 * @param anchor the anchor (<code>null</code> not permitted). 432 * 433 * @see #getLabelAnchor() 434 */ 435 public void setLabelAnchor(RectangleAnchor anchor) { 436 if (anchor == null) { 437 throw new IllegalArgumentException("Null 'anchor' argument."); 438 } 439 this.labelAnchor = anchor; 440 notifyListeners(new MarkerChangeEvent(this)); 441 } 442 443 /** 444 * Returns the label offset. 445 * 446 * @return The label offset (never <code>null</code>). 447 * 448 * @see #setLabelOffset(RectangleInsets) 449 */ 450 public RectangleInsets getLabelOffset() { 451 return this.labelOffset; 452 } 453 454 /** 455 * Sets the label offset and sends a {@link MarkerChangeEvent} to all 456 * registered listeners. 457 * 458 * @param offset the label offset (<code>null</code> not permitted). 459 * 460 * @see #getLabelOffset() 461 */ 462 public void setLabelOffset(RectangleInsets offset) { 463 if (offset == null) { 464 throw new IllegalArgumentException("Null 'offset' argument."); 465 } 466 this.labelOffset = offset; 467 notifyListeners(new MarkerChangeEvent(this)); 468 } 469 470 /** 471 * Returns the label offset type. 472 * 473 * @return The type (never <code>null</code>). 474 * 475 * @see #setLabelOffsetType(LengthAdjustmentType) 476 */ 477 public LengthAdjustmentType getLabelOffsetType() { 478 return this.labelOffsetType; 479 } 480 481 /** 482 * Sets the label offset type and sends a {@link MarkerChangeEvent} to all 483 * registered listeners. 484 * 485 * @param adj the type (<code>null</code> not permitted). 486 * 487 * @see #getLabelOffsetType() 488 */ 489 public void setLabelOffsetType(LengthAdjustmentType adj) { 490 if (adj == null) { 491 throw new IllegalArgumentException("Null 'adj' argument."); 492 } 493 this.labelOffsetType = adj; 494 notifyListeners(new MarkerChangeEvent(this)); 495 } 496 497 /** 498 * Returns the label text anchor. 499 * 500 * @return The label text anchor (never <code>null</code>). 501 * 502 * @see #setLabelTextAnchor(TextAnchor) 503 */ 504 public TextAnchor getLabelTextAnchor() { 505 return this.labelTextAnchor; 506 } 507 508 /** 509 * Sets the label text anchor and sends a {@link MarkerChangeEvent} to 510 * all registered listeners. 511 * 512 * @param anchor the label text anchor (<code>null</code> not permitted). 513 * 514 * @see #getLabelTextAnchor() 515 */ 516 public void setLabelTextAnchor(TextAnchor anchor) { 517 if (anchor == null) { 518 throw new IllegalArgumentException("Null 'anchor' argument."); 519 } 520 this.labelTextAnchor = anchor; 521 notifyListeners(new MarkerChangeEvent(this)); 522 } 523 524 /** 525 * Registers an object for notification of changes to the marker. 526 * 527 * @param listener the object to be registered. 528 * 529 * @since 1.0.3 530 */ 531 public void addChangeListener(MarkerChangeListener listener) { 532 this.listenerList.add(MarkerChangeListener.class, listener); 533 } 534 535 /** 536 * Unregisters an object for notification of changes to the marker. 537 * 538 * @param listener the object to be unregistered. 539 * 540 * @since 1.0.3 541 */ 542 public void removeChangeListener(MarkerChangeListener listener) { 543 this.listenerList.remove(MarkerChangeListener.class, listener); 544 } 545 546 /** 547 * Notifies all registered listeners that the marker has been modified. 548 * 549 * @param event information about the change event. 550 * 551 * @since 1.0.3 552 */ 553 public void notifyListeners(MarkerChangeEvent event) { 554 555 Object[] listeners = this.listenerList.getListenerList(); 556 for (int i = listeners.length - 2; i >= 0; i -= 2) { 557 if (listeners[i] == MarkerChangeListener.class) { 558 ((MarkerChangeListener) listeners[i + 1]).markerChanged(event); 559 } 560 } 561 562 } 563 564 /** 565 * Returns an array containing all the listeners of the specified type. 566 * 567 * @param listenerType the listener type. 568 * 569 * @return The array of listeners. 570 * 571 * @since 1.0.3 572 */ 573 public EventListener[] getListeners(Class listenerType) { 574 return this.listenerList.getListeners(listenerType); 575 } 576 577 /** 578 * Tests the marker for equality with an arbitrary object. 579 * 580 * @param obj the object (<code>null</code> permitted). 581 * 582 * @return A boolean. 583 */ 584 public boolean equals(Object obj) { 585 if (obj == this) { 586 return true; 587 } 588 if (!(obj instanceof Marker)) { 589 return false; 590 } 591 Marker that = (Marker) obj; 592 if (!PaintUtilities.equal(this.paint, that.paint)) { 593 return false; 594 } 595 if (!ObjectUtilities.equal(this.stroke, that.stroke)) { 596 return false; 597 } 598 if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) { 599 return false; 600 } 601 if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) { 602 return false; 603 } 604 if (this.alpha != that.alpha) { 605 return false; 606 } 607 if (!ObjectUtilities.equal(this.label, that.label)) { 608 return false; 609 } 610 if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) { 611 return false; 612 } 613 if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) { 614 return false; 615 } 616 if (this.labelAnchor != that.labelAnchor) { 617 return false; 618 } 619 if (this.labelTextAnchor != that.labelTextAnchor) { 620 return false; 621 } 622 if (!ObjectUtilities.equal(this.labelOffset, that.labelOffset)) { 623 return false; 624 } 625 if (!this.labelOffsetType.equals(that.labelOffsetType)) { 626 return false; 627 } 628 return true; 629 } 630 631 /** 632 * Creates a clone of the marker. 633 * 634 * @return A clone. 635 * 636 * @throws CloneNotSupportedException never. 637 */ 638 public Object clone() throws CloneNotSupportedException { 639 return super.clone(); 640 } 641 642 /** 643 * Provides serialization support. 644 * 645 * @param stream the output stream. 646 * 647 * @throws IOException if there is an I/O error. 648 */ 649 private void writeObject(ObjectOutputStream stream) throws IOException { 650 stream.defaultWriteObject(); 651 SerialUtilities.writePaint(this.paint, stream); 652 SerialUtilities.writeStroke(this.stroke, stream); 653 SerialUtilities.writePaint(this.outlinePaint, stream); 654 SerialUtilities.writeStroke(this.outlineStroke, stream); 655 SerialUtilities.writePaint(this.labelPaint, stream); 656 } 657 658 /** 659 * Provides serialization support. 660 * 661 * @param stream the input stream. 662 * 663 * @throws IOException if there is an I/O error. 664 * @throws ClassNotFoundException if there is a classpath problem. 665 */ 666 private void readObject(ObjectInputStream stream) 667 throws IOException, ClassNotFoundException { 668 stream.defaultReadObject(); 669 this.paint = SerialUtilities.readPaint(stream); 670 this.stroke = SerialUtilities.readStroke(stream); 671 this.outlinePaint = SerialUtilities.readPaint(stream); 672 this.outlineStroke = SerialUtilities.readStroke(stream); 673 this.labelPaint = SerialUtilities.readPaint(stream); 674 } 675 676 }