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