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