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