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