001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2011, 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 * Axis.java 029 * --------- 030 * (C) Copyright 2000-2009, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Bill Kelemen; 034 * Nicolas Brodu; 035 * Peter Kolb (patches 1934255 and 2603321); 036 * Andrew Mickish (patch 1870189); 037 * 038 * Changes 039 * ------- 040 * 21-Aug-2001 : Added standard header, fixed DOS encoding problem (DG); 041 * 18-Sep-2001 : Updated header (DG); 042 * 07-Nov-2001 : Allow null axis labels (DG); 043 * : Added default font values (DG); 044 * 13-Nov-2001 : Modified the setPlot() method to check compatibility between 045 * the axis and the plot (DG); 046 * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG); 047 * 06-Dec-2001 : Allow null in setPlot() method (BK); 048 * 06-Mar-2002 : Added AxisConstants interface (DG); 049 * 23-Apr-2002 : Added a visible property. Moved drawVerticalString to 050 * RefineryUtilities. Added fixedDimension property for use in 051 * combined plots (DG); 052 * 25-Jun-2002 : Removed unnecessary imports (DG); 053 * 05-Sep-2002 : Added attribute for tick mark paint (DG); 054 * 18-Sep-2002 : Fixed errors reported by Checkstyle (DG); 055 * 07-Nov-2002 : Added attributes to control the inside and outside length of 056 * the tick marks (DG); 057 * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG); 058 * 18-Nov-2002 : Added axis location to refreshTicks() parameters (DG); 059 * 15-Jan-2003 : Removed monolithic constructor (DG); 060 * 17-Jan-2003 : Moved plot classes to separate package (DG); 061 * 26-Mar-2003 : Implemented Serializable (DG); 062 * 03-Jul-2003 : Modified reserveSpace method (DG); 063 * 13-Aug-2003 : Implemented Cloneable (DG); 064 * 11-Sep-2003 : Took care of listeners while cloning (NB); 065 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 066 * 06-Nov-2003 : Modified refreshTicks() signature (DG); 067 * 06-Jan-2004 : Added axis line attributes (DG); 068 * 16-Mar-2004 : Added plot state to draw() method (DG); 069 * 07-Apr-2004 : Modified text bounds calculation (DG); 070 * 18-May-2004 : Eliminated AxisConstants.java (DG); 071 * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities --> 072 * TextUtilities (DG); 073 * 04-Oct-2004 : Modified getLabelEnclosure() method to treat an empty String 074 * the same way as a null string - see bug 1026521 (DG); 075 * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG); 076 * 26-Apr-2005 : Removed LOGGER (DG); 077 * 01-Jun-2005 : Added hasListener() method for unit testing (DG); 078 * 08-Jun-2005 : Fixed equals() method to handle GradientPaint (DG); 079 * ------------- JFREECHART 1.0.x --------------------------------------------- 080 * 22-Aug-2006 : API doc updates (DG); 081 * 06-Jun-2008 : Added setTickLabelInsets(RectangleInsets, boolean) (DG); 082 * 25-Sep-2008 : Added minor tick support, see patch 1934255 by Peter Kolb (DG); 083 * 26-Sep-2008 : Added fireChangeEvent() method (DG); 084 * 19-Mar-2009 : Added entity support - see patch 2603321 by Peter Kolb (DG); 085 * 086 */ 087 088package org.jfree.chart.axis; 089 090import java.awt.BasicStroke; 091import java.awt.Color; 092import java.awt.Font; 093import java.awt.FontMetrics; 094import java.awt.Graphics2D; 095import java.awt.Paint; 096import java.awt.Shape; 097import java.awt.Stroke; 098import java.awt.geom.AffineTransform; 099import java.awt.geom.Line2D; 100import java.awt.geom.Rectangle2D; 101import java.io.IOException; 102import java.io.ObjectInputStream; 103import java.io.ObjectOutputStream; 104import java.io.Serializable; 105import java.util.Arrays; 106import java.util.EventListener; 107import java.util.List; 108 109import javax.swing.event.EventListenerList; 110 111import org.jfree.chart.entity.AxisEntity; 112import org.jfree.chart.entity.EntityCollection; 113import org.jfree.chart.event.AxisChangeEvent; 114import org.jfree.chart.event.AxisChangeListener; 115import org.jfree.chart.plot.Plot; 116import org.jfree.chart.plot.PlotRenderingInfo; 117import org.jfree.io.SerialUtilities; 118import org.jfree.text.TextUtilities; 119import org.jfree.ui.RectangleEdge; 120import org.jfree.ui.RectangleInsets; 121import org.jfree.ui.TextAnchor; 122import org.jfree.util.ObjectUtilities; 123import org.jfree.util.PaintUtilities; 124 125/** 126 * The base class for all axes in JFreeChart. Subclasses are divided into 127 * those that display values ({@link ValueAxis}) and those that display 128 * categories ({@link CategoryAxis}). 129 */ 130public abstract class Axis implements Cloneable, Serializable { 131 132 /** For serialization. */ 133 private static final long serialVersionUID = 7719289504573298271L; 134 135 /** The default axis visibility. */ 136 public static final boolean DEFAULT_AXIS_VISIBLE = true; 137 138 /** The default axis label font. */ 139 public static final Font DEFAULT_AXIS_LABEL_FONT = new Font( 140 "SansSerif", Font.PLAIN, 12); 141 142 /** The default axis label paint. */ 143 public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.black; 144 145 /** The default axis label insets. */ 146 public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS 147 = new RectangleInsets(3.0, 3.0, 3.0, 3.0); 148 149 /** The default axis line paint. */ 150 public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.gray; 151 152 /** The default axis line stroke. */ 153 public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(1.0f); 154 155 /** The default tick labels visibility. */ 156 public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true; 157 158 /** The default tick label font. */ 159 public static final Font DEFAULT_TICK_LABEL_FONT = new Font("SansSerif", 160 Font.PLAIN, 10); 161 162 /** The default tick label paint. */ 163 public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.black; 164 165 /** The default tick label insets. */ 166 public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS 167 = new RectangleInsets(2.0, 4.0, 2.0, 4.0); 168 169 /** The default tick marks visible. */ 170 public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true; 171 172 /** The default tick stroke. */ 173 public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(1); 174 175 /** The default tick paint. */ 176 public static final Paint DEFAULT_TICK_MARK_PAINT = Color.gray; 177 178 /** The default tick mark inside length. */ 179 public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f; 180 181 /** The default tick mark outside length. */ 182 public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f; 183 184 /** A flag indicating whether or not the axis is visible. */ 185 private boolean visible; 186 187 /** The label for the axis. */ 188 private String label; 189 190 /** The font for displaying the axis label. */ 191 private Font labelFont; 192 193 /** The paint for drawing the axis label. */ 194 private transient Paint labelPaint; 195 196 /** The insets for the axis label. */ 197 private RectangleInsets labelInsets; 198 199 /** The label angle. */ 200 private double labelAngle; 201 202 /** A flag that controls whether or not the axis line is visible. */ 203 private boolean axisLineVisible; 204 205 /** The stroke used for the axis line. */ 206 private transient Stroke axisLineStroke; 207 208 /** The paint used for the axis line. */ 209 private transient Paint axisLinePaint; 210 211 /** 212 * A flag that indicates whether or not tick labels are visible for the 213 * axis. 214 */ 215 private boolean tickLabelsVisible; 216 217 /** The font used to display the tick labels. */ 218 private Font tickLabelFont; 219 220 /** The color used to display the tick labels. */ 221 private transient Paint tickLabelPaint; 222 223 /** The blank space around each tick label. */ 224 private RectangleInsets tickLabelInsets; 225 226 /** 227 * A flag that indicates whether or not major tick marks are visible for 228 * the axis. 229 */ 230 private boolean tickMarksVisible; 231 232 /** 233 * The length of the major tick mark inside the data area (zero 234 * permitted). 235 */ 236 private float tickMarkInsideLength; 237 238 /** 239 * The length of the major tick mark outside the data area (zero 240 * permitted). 241 */ 242 private float tickMarkOutsideLength; 243 244 /** 245 * A flag that indicates whether or not minor tick marks are visible for the 246 * axis. 247 * 248 * @since 1.0.12 249 */ 250 private boolean minorTickMarksVisible; 251 252 /** 253 * The length of the minor tick mark inside the data area (zero permitted). 254 * 255 * @since 1.0.12 256 */ 257 private float minorTickMarkInsideLength; 258 259 /** 260 * The length of the minor tick mark outside the data area (zero permitted). 261 * 262 * @since 1.0.12 263 */ 264 private float minorTickMarkOutsideLength; 265 266 /** The stroke used to draw tick marks. */ 267 private transient Stroke tickMarkStroke; 268 269 /** The paint used to draw tick marks. */ 270 private transient Paint tickMarkPaint; 271 272 /** The fixed (horizontal or vertical) dimension for the axis. */ 273 private double fixedDimension; 274 275 /** 276 * A reference back to the plot that the axis is assigned to (can be 277 * <code>null</code>). 278 */ 279 private transient Plot plot; 280 281 /** Storage for registered listeners. */ 282 private transient EventListenerList listenerList; 283 284 /** 285 * Constructs an axis, using default values where necessary. 286 * 287 * @param label the axis label (<code>null</code> permitted). 288 */ 289 protected Axis(String label) { 290 291 this.label = label; 292 this.visible = DEFAULT_AXIS_VISIBLE; 293 this.labelFont = DEFAULT_AXIS_LABEL_FONT; 294 this.labelPaint = DEFAULT_AXIS_LABEL_PAINT; 295 this.labelInsets = DEFAULT_AXIS_LABEL_INSETS; 296 this.labelAngle = 0.0; 297 298 this.axisLineVisible = true; 299 this.axisLinePaint = DEFAULT_AXIS_LINE_PAINT; 300 this.axisLineStroke = DEFAULT_AXIS_LINE_STROKE; 301 302 this.tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE; 303 this.tickLabelFont = DEFAULT_TICK_LABEL_FONT; 304 this.tickLabelPaint = DEFAULT_TICK_LABEL_PAINT; 305 this.tickLabelInsets = DEFAULT_TICK_LABEL_INSETS; 306 307 this.tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE; 308 this.tickMarkStroke = DEFAULT_TICK_MARK_STROKE; 309 this.tickMarkPaint = DEFAULT_TICK_MARK_PAINT; 310 this.tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH; 311 this.tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH; 312 313 this.minorTickMarksVisible = false; 314 this.minorTickMarkInsideLength = 0.0f; 315 this.minorTickMarkOutsideLength = 2.0f; 316 317 this.plot = null; 318 319 this.listenerList = new EventListenerList(); 320 321 } 322 323 /** 324 * Returns <code>true</code> if the axis is visible, and 325 * <code>false</code> otherwise. 326 * 327 * @return A boolean. 328 * 329 * @see #setVisible(boolean) 330 */ 331 public boolean isVisible() { 332 return this.visible; 333 } 334 335 /** 336 * Sets a flag that controls whether or not the axis is visible and sends 337 * an {@link AxisChangeEvent} to all registered listeners. 338 * 339 * @param flag the flag. 340 * 341 * @see #isVisible() 342 */ 343 public void setVisible(boolean flag) { 344 if (flag != this.visible) { 345 this.visible = flag; 346 fireChangeEvent(); 347 } 348 } 349 350 /** 351 * Returns the label for the axis. 352 * 353 * @return The label for the axis (<code>null</code> possible). 354 * 355 * @see #getLabelFont() 356 * @see #getLabelPaint() 357 * @see #setLabel(String) 358 */ 359 public String getLabel() { 360 return this.label; 361 } 362 363 /** 364 * Sets the label for the axis and sends an {@link AxisChangeEvent} to all 365 * registered listeners. 366 * 367 * @param label the new label (<code>null</code> permitted). 368 * 369 * @see #getLabel() 370 * @see #setLabelFont(Font) 371 * @see #setLabelPaint(Paint) 372 */ 373 public void setLabel(String label) { 374 375 String existing = this.label; 376 if (existing != null) { 377 if (!existing.equals(label)) { 378 this.label = label; 379 fireChangeEvent(); 380 } 381 } 382 else { 383 if (label != null) { 384 this.label = label; 385 fireChangeEvent(); 386 } 387 } 388 389 } 390 391 /** 392 * Returns the font for the axis label. 393 * 394 * @return The font (never <code>null</code>). 395 * 396 * @see #setLabelFont(Font) 397 */ 398 public Font getLabelFont() { 399 return this.labelFont; 400 } 401 402 /** 403 * Sets the font for the axis label and sends an {@link AxisChangeEvent} 404 * to all registered listeners. 405 * 406 * @param font the font (<code>null</code> not permitted). 407 * 408 * @see #getLabelFont() 409 */ 410 public void setLabelFont(Font font) { 411 if (font == null) { 412 throw new IllegalArgumentException("Null 'font' argument."); 413 } 414 if (!this.labelFont.equals(font)) { 415 this.labelFont = font; 416 fireChangeEvent(); 417 } 418 } 419 420 /** 421 * Returns the color/shade used to draw the axis label. 422 * 423 * @return The paint (never <code>null</code>). 424 * 425 * @see #setLabelPaint(Paint) 426 */ 427 public Paint getLabelPaint() { 428 return this.labelPaint; 429 } 430 431 /** 432 * Sets the paint used to draw the axis label and sends an 433 * {@link AxisChangeEvent} to all registered listeners. 434 * 435 * @param paint the paint (<code>null</code> not permitted). 436 * 437 * @see #getLabelPaint() 438 */ 439 public void setLabelPaint(Paint paint) { 440 if (paint == null) { 441 throw new IllegalArgumentException("Null 'paint' argument."); 442 } 443 this.labelPaint = paint; 444 fireChangeEvent(); 445 } 446 447 /** 448 * Returns the insets for the label (that is, the amount of blank space 449 * that should be left around the label). 450 * 451 * @return The label insets (never <code>null</code>). 452 * 453 * @see #setLabelInsets(RectangleInsets) 454 */ 455 public RectangleInsets getLabelInsets() { 456 return this.labelInsets; 457 } 458 459 /** 460 * Sets the insets for the axis label, and sends an {@link AxisChangeEvent} 461 * to all registered listeners. 462 * 463 * @param insets the insets (<code>null</code> not permitted). 464 * 465 * @see #getLabelInsets() 466 */ 467 public void setLabelInsets(RectangleInsets insets) { 468 setLabelInsets(insets, true); 469 } 470 471 /** 472 * Sets the insets for the axis label, and sends an {@link AxisChangeEvent} 473 * to all registered listeners. 474 * 475 * @param insets the insets (<code>null</code> not permitted). 476 * @param notify notify listeners? 477 * 478 * @since 1.0.10 479 */ 480 public void setLabelInsets(RectangleInsets insets, boolean notify) { 481 if (insets == null) { 482 throw new IllegalArgumentException("Null 'insets' argument."); 483 } 484 if (!insets.equals(this.labelInsets)) { 485 this.labelInsets = insets; 486 if (notify) { 487 fireChangeEvent(); 488 } 489 } 490 } 491 492 /** 493 * Returns the angle of the axis label. 494 * 495 * @return The angle (in radians). 496 * 497 * @see #setLabelAngle(double) 498 */ 499 public double getLabelAngle() { 500 return this.labelAngle; 501 } 502 503 /** 504 * Sets the angle for the label and sends an {@link AxisChangeEvent} to all 505 * registered listeners. 506 * 507 * @param angle the angle (in radians). 508 * 509 * @see #getLabelAngle() 510 */ 511 public void setLabelAngle(double angle) { 512 this.labelAngle = angle; 513 fireChangeEvent(); 514 } 515 516 /** 517 * A flag that controls whether or not the axis line is drawn. 518 * 519 * @return A boolean. 520 * 521 * @see #getAxisLinePaint() 522 * @see #getAxisLineStroke() 523 * @see #setAxisLineVisible(boolean) 524 */ 525 public boolean isAxisLineVisible() { 526 return this.axisLineVisible; 527 } 528 529 /** 530 * Sets a flag that controls whether or not the axis line is visible and 531 * sends an {@link AxisChangeEvent} to all registered listeners. 532 * 533 * @param visible the flag. 534 * 535 * @see #isAxisLineVisible() 536 * @see #setAxisLinePaint(Paint) 537 * @see #setAxisLineStroke(Stroke) 538 */ 539 public void setAxisLineVisible(boolean visible) { 540 this.axisLineVisible = visible; 541 fireChangeEvent(); 542 } 543 544 /** 545 * Returns the paint used to draw the axis line. 546 * 547 * @return The paint (never <code>null</code>). 548 * 549 * @see #setAxisLinePaint(Paint) 550 */ 551 public Paint getAxisLinePaint() { 552 return this.axisLinePaint; 553 } 554 555 /** 556 * Sets the paint used to draw the axis line and sends an 557 * {@link AxisChangeEvent} to all registered listeners. 558 * 559 * @param paint the paint (<code>null</code> not permitted). 560 * 561 * @see #getAxisLinePaint() 562 */ 563 public void setAxisLinePaint(Paint paint) { 564 if (paint == null) { 565 throw new IllegalArgumentException("Null 'paint' argument."); 566 } 567 this.axisLinePaint = paint; 568 fireChangeEvent(); 569 } 570 571 /** 572 * Returns the stroke used to draw the axis line. 573 * 574 * @return The stroke (never <code>null</code>). 575 * 576 * @see #setAxisLineStroke(Stroke) 577 */ 578 public Stroke getAxisLineStroke() { 579 return this.axisLineStroke; 580 } 581 582 /** 583 * Sets the stroke used to draw the axis line and sends an 584 * {@link AxisChangeEvent} to all registered listeners. 585 * 586 * @param stroke the stroke (<code>null</code> not permitted). 587 * 588 * @see #getAxisLineStroke() 589 */ 590 public void setAxisLineStroke(Stroke stroke) { 591 if (stroke == null) { 592 throw new IllegalArgumentException("Null 'stroke' argument."); 593 } 594 this.axisLineStroke = stroke; 595 fireChangeEvent(); 596 } 597 598 /** 599 * Returns a flag indicating whether or not the tick labels are visible. 600 * 601 * @return The flag. 602 * 603 * @see #getTickLabelFont() 604 * @see #getTickLabelPaint() 605 * @see #setTickLabelsVisible(boolean) 606 */ 607 public boolean isTickLabelsVisible() { 608 return this.tickLabelsVisible; 609 } 610 611 /** 612 * Sets the flag that determines whether or not the tick labels are 613 * visible and sends an {@link AxisChangeEvent} to all registered 614 * listeners. 615 * 616 * @param flag the flag. 617 * 618 * @see #isTickLabelsVisible() 619 * @see #setTickLabelFont(Font) 620 * @see #setTickLabelPaint(Paint) 621 */ 622 public void setTickLabelsVisible(boolean flag) { 623 624 if (flag != this.tickLabelsVisible) { 625 this.tickLabelsVisible = flag; 626 fireChangeEvent(); 627 } 628 629 } 630 631 /** 632 * Returns the flag that indicates whether or not the minor tick marks are 633 * showing. 634 * 635 * @return The flag that indicates whether or not the minor tick marks are 636 * showing. 637 * 638 * @see #setMinorTickMarksVisible(boolean) 639 * 640 * @since 1.0.12 641 */ 642 public boolean isMinorTickMarksVisible() { 643 return this.minorTickMarksVisible; 644 } 645 646 /** 647 * Sets the flag that indicates whether or not the minor tick marks are 648 * showing and sends an {@link AxisChangeEvent} to all registered 649 * listeners. 650 * 651 * @param flag the flag. 652 * 653 * @see #isMinorTickMarksVisible() 654 * 655 * @since 1.0.12 656 */ 657 public void setMinorTickMarksVisible(boolean flag) { 658 if (flag != this.minorTickMarksVisible) { 659 this.minorTickMarksVisible = flag; 660 fireChangeEvent(); 661 } 662 } 663 664 /** 665 * Returns the font used for the tick labels (if showing). 666 * 667 * @return The font (never <code>null</code>). 668 * 669 * @see #setTickLabelFont(Font) 670 */ 671 public Font getTickLabelFont() { 672 return this.tickLabelFont; 673 } 674 675 /** 676 * Sets the font for the tick labels and sends an {@link AxisChangeEvent} 677 * to all registered listeners. 678 * 679 * @param font the font (<code>null</code> not allowed). 680 * 681 * @see #getTickLabelFont() 682 */ 683 public void setTickLabelFont(Font font) { 684 685 if (font == null) { 686 throw new IllegalArgumentException("Null 'font' argument."); 687 } 688 689 if (!this.tickLabelFont.equals(font)) { 690 this.tickLabelFont = font; 691 fireChangeEvent(); 692 } 693 694 } 695 696 /** 697 * Returns the color/shade used for the tick labels. 698 * 699 * @return The paint used for the tick labels. 700 * 701 * @see #setTickLabelPaint(Paint) 702 */ 703 public Paint getTickLabelPaint() { 704 return this.tickLabelPaint; 705 } 706 707 /** 708 * Sets the paint used to draw tick labels (if they are showing) and 709 * sends an {@link AxisChangeEvent} to all registered listeners. 710 * 711 * @param paint the paint (<code>null</code> not permitted). 712 * 713 * @see #getTickLabelPaint() 714 */ 715 public void setTickLabelPaint(Paint paint) { 716 if (paint == null) { 717 throw new IllegalArgumentException("Null 'paint' argument."); 718 } 719 this.tickLabelPaint = paint; 720 fireChangeEvent(); 721 } 722 723 /** 724 * Returns the insets for the tick labels. 725 * 726 * @return The insets (never <code>null</code>). 727 * 728 * @see #setTickLabelInsets(RectangleInsets) 729 */ 730 public RectangleInsets getTickLabelInsets() { 731 return this.tickLabelInsets; 732 } 733 734 /** 735 * Sets the insets for the tick labels and sends an {@link AxisChangeEvent} 736 * to all registered listeners. 737 * 738 * @param insets the insets (<code>null</code> not permitted). 739 * 740 * @see #getTickLabelInsets() 741 */ 742 public void setTickLabelInsets(RectangleInsets insets) { 743 if (insets == null) { 744 throw new IllegalArgumentException("Null 'insets' argument."); 745 } 746 if (!this.tickLabelInsets.equals(insets)) { 747 this.tickLabelInsets = insets; 748 fireChangeEvent(); 749 } 750 } 751 752 /** 753 * Returns the flag that indicates whether or not the tick marks are 754 * showing. 755 * 756 * @return The flag that indicates whether or not the tick marks are 757 * showing. 758 * 759 * @see #setTickMarksVisible(boolean) 760 */ 761 public boolean isTickMarksVisible() { 762 return this.tickMarksVisible; 763 } 764 765 /** 766 * Sets the flag that indicates whether or not the tick marks are showing 767 * and sends an {@link AxisChangeEvent} to all registered listeners. 768 * 769 * @param flag the flag. 770 * 771 * @see #isTickMarksVisible() 772 */ 773 public void setTickMarksVisible(boolean flag) { 774 if (flag != this.tickMarksVisible) { 775 this.tickMarksVisible = flag; 776 fireChangeEvent(); 777 } 778 } 779 780 /** 781 * Returns the inside length of the tick marks. 782 * 783 * @return The length. 784 * 785 * @see #getTickMarkOutsideLength() 786 * @see #setTickMarkInsideLength(float) 787 */ 788 public float getTickMarkInsideLength() { 789 return this.tickMarkInsideLength; 790 } 791 792 /** 793 * Sets the inside length of the tick marks and sends 794 * an {@link AxisChangeEvent} to all registered listeners. 795 * 796 * @param length the new length. 797 * 798 * @see #getTickMarkInsideLength() 799 */ 800 public void setTickMarkInsideLength(float length) { 801 this.tickMarkInsideLength = length; 802 fireChangeEvent(); 803 } 804 805 /** 806 * Returns the outside length of the tick marks. 807 * 808 * @return The length. 809 * 810 * @see #getTickMarkInsideLength() 811 * @see #setTickMarkOutsideLength(float) 812 */ 813 public float getTickMarkOutsideLength() { 814 return this.tickMarkOutsideLength; 815 } 816 817 /** 818 * Sets the outside length of the tick marks and sends 819 * an {@link AxisChangeEvent} to all registered listeners. 820 * 821 * @param length the new length. 822 * 823 * @see #getTickMarkInsideLength() 824 */ 825 public void setTickMarkOutsideLength(float length) { 826 this.tickMarkOutsideLength = length; 827 fireChangeEvent(); 828 } 829 830 /** 831 * Returns the stroke used to draw tick marks. 832 * 833 * @return The stroke (never <code>null</code>). 834 * 835 * @see #setTickMarkStroke(Stroke) 836 */ 837 public Stroke getTickMarkStroke() { 838 return this.tickMarkStroke; 839 } 840 841 /** 842 * Sets the stroke used to draw tick marks and sends 843 * an {@link AxisChangeEvent} to all registered listeners. 844 * 845 * @param stroke the stroke (<code>null</code> not permitted). 846 * 847 * @see #getTickMarkStroke() 848 */ 849 public void setTickMarkStroke(Stroke stroke) { 850 if (stroke == null) { 851 throw new IllegalArgumentException("Null 'stroke' argument."); 852 } 853 if (!this.tickMarkStroke.equals(stroke)) { 854 this.tickMarkStroke = stroke; 855 fireChangeEvent(); 856 } 857 } 858 859 /** 860 * Returns the paint used to draw tick marks (if they are showing). 861 * 862 * @return The paint (never <code>null</code>). 863 * 864 * @see #setTickMarkPaint(Paint) 865 */ 866 public Paint getTickMarkPaint() { 867 return this.tickMarkPaint; 868 } 869 870 /** 871 * Sets the paint used to draw tick marks and sends an 872 * {@link AxisChangeEvent} to all registered listeners. 873 * 874 * @param paint the paint (<code>null</code> not permitted). 875 * 876 * @see #getTickMarkPaint() 877 */ 878 public void setTickMarkPaint(Paint paint) { 879 if (paint == null) { 880 throw new IllegalArgumentException("Null 'paint' argument."); 881 } 882 this.tickMarkPaint = paint; 883 fireChangeEvent(); 884 } 885 886 /** 887 * Returns the inside length of the minor tick marks. 888 * 889 * @return The length. 890 * 891 * @see #getMinorTickMarkOutsideLength() 892 * @see #setMinorTickMarkInsideLength(float) 893 * 894 * @since 1.0.12 895 */ 896 public float getMinorTickMarkInsideLength() { 897 return this.minorTickMarkInsideLength; 898 } 899 900 /** 901 * Sets the inside length of the minor tick marks and sends 902 * an {@link AxisChangeEvent} to all registered listeners. 903 * 904 * @param length the new length. 905 * 906 * @see #getMinorTickMarkInsideLength() 907 * 908 * @since 1.0.12 909 */ 910 public void setMinorTickMarkInsideLength(float length) { 911 this.minorTickMarkInsideLength = length; 912 fireChangeEvent(); 913 } 914 915 /** 916 * Returns the outside length of the minor tick marks. 917 * 918 * @return The length. 919 * 920 * @see #getMinorTickMarkInsideLength() 921 * @see #setMinorTickMarkOutsideLength(float) 922 * 923 * @since 1.0.12 924 */ 925 public float getMinorTickMarkOutsideLength() { 926 return this.minorTickMarkOutsideLength; 927 } 928 929 /** 930 * Sets the outside length of the minor tick marks and sends 931 * an {@link AxisChangeEvent} to all registered listeners. 932 * 933 * @param length the new length. 934 * 935 * @see #getMinorTickMarkInsideLength() 936 * 937 * @since 1.0.12 938 */ 939 public void setMinorTickMarkOutsideLength(float length) { 940 this.minorTickMarkOutsideLength = length; 941 fireChangeEvent(); 942 } 943 944 /** 945 * Returns the plot that the axis is assigned to. This method will return 946 * <code>null</code> if the axis is not currently assigned to a plot. 947 * 948 * @return The plot that the axis is assigned to (possibly 949 * <code>null</code>). 950 * 951 * @see #setPlot(Plot) 952 */ 953 public Plot getPlot() { 954 return this.plot; 955 } 956 957 /** 958 * Sets a reference to the plot that the axis is assigned to. 959 * <P> 960 * This method is used internally, you shouldn't need to call it yourself. 961 * 962 * @param plot the plot. 963 * 964 * @see #getPlot() 965 */ 966 public void setPlot(Plot plot) { 967 this.plot = plot; 968 configure(); 969 } 970 971 /** 972 * Returns the fixed dimension for the axis. 973 * 974 * @return The fixed dimension. 975 * 976 * @see #setFixedDimension(double) 977 */ 978 public double getFixedDimension() { 979 return this.fixedDimension; 980 } 981 982 /** 983 * Sets the fixed dimension for the axis. 984 * <P> 985 * This is used when combining more than one plot on a chart. In this case, 986 * there may be several axes that need to have the same height or width so 987 * that they are aligned. This method is used to fix a dimension for the 988 * axis (the context determines whether the dimension is horizontal or 989 * vertical). 990 * 991 * @param dimension the fixed dimension. 992 * 993 * @see #getFixedDimension() 994 */ 995 public void setFixedDimension(double dimension) { 996 this.fixedDimension = dimension; 997 } 998 999 /** 1000 * Configures the axis to work with the current plot. Override this method 1001 * to perform any special processing (such as auto-rescaling). 1002 */ 1003 public abstract void configure(); 1004 1005 /** 1006 * Estimates the space (height or width) required to draw the axis. 1007 * 1008 * @param g2 the graphics device. 1009 * @param plot the plot that the axis belongs to. 1010 * @param plotArea the area within which the plot (including axes) should 1011 * be drawn. 1012 * @param edge the axis location. 1013 * @param space space already reserved. 1014 * 1015 * @return The space required to draw the axis (including pre-reserved 1016 * space). 1017 */ 1018 public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot, 1019 Rectangle2D plotArea, 1020 RectangleEdge edge, 1021 AxisSpace space); 1022 1023 /** 1024 * Draws the axis on a Java 2D graphics device (such as the screen or a 1025 * printer). 1026 * 1027 * @param g2 the graphics device (<code>null</code> not permitted). 1028 * @param cursor the cursor location (determines where to draw the axis). 1029 * @param plotArea the area within which the axes and plot should be drawn. 1030 * @param dataArea the area within which the data should be drawn. 1031 * @param edge the axis location (<code>null</code> not permitted). 1032 * @param plotState collects information about the plot 1033 * (<code>null</code> permitted). 1034 * 1035 * @return The axis state (never <code>null</code>). 1036 */ 1037 public abstract AxisState draw(Graphics2D g2, 1038 double cursor, 1039 Rectangle2D plotArea, 1040 Rectangle2D dataArea, 1041 RectangleEdge edge, 1042 PlotRenderingInfo plotState); 1043 1044 /** 1045 * Calculates the positions of the ticks for the axis, storing the results 1046 * in the tick list (ready for drawing). 1047 * 1048 * @param g2 the graphics device. 1049 * @param state the axis state. 1050 * @param dataArea the area inside the axes. 1051 * @param edge the edge on which the axis is located. 1052 * 1053 * @return The list of ticks. 1054 */ 1055 public abstract List refreshTicks(Graphics2D g2, AxisState state, 1056 Rectangle2D dataArea, RectangleEdge edge); 1057 1058 /** 1059 * Created an entity for the axis. 1060 * 1061 * @param cursor the initial cursor value. 1062 * @param state the axis state after completion of the drawing with a 1063 * possibly updated cursor position. 1064 * @param dataArea the data area. 1065 * @param edge the edge. 1066 * @param plotState the PlotRenderingInfo from which a reference to the 1067 * entity collection can be obtained. 1068 * 1069 * @since 1.0.13 1070 */ 1071 protected void createAndAddEntity(double cursor, AxisState state, 1072 Rectangle2D dataArea, RectangleEdge edge, 1073 PlotRenderingInfo plotState) { 1074 1075 if (plotState == null || plotState.getOwner() == null) { 1076 return; // no need to create entity if we can't save it anyways... 1077 } 1078 Rectangle2D hotspot = null; 1079 if (edge.equals(RectangleEdge.TOP)) { 1080 hotspot = new Rectangle2D.Double(dataArea.getX(), 1081 state.getCursor(), dataArea.getWidth(), 1082 cursor - state.getCursor()); 1083 } 1084 else if (edge.equals(RectangleEdge.BOTTOM)) { 1085 hotspot = new Rectangle2D.Double(dataArea.getX(), cursor, 1086 dataArea.getWidth(), state.getCursor() - cursor); 1087 } 1088 else if (edge.equals(RectangleEdge.LEFT)) { 1089 hotspot = new Rectangle2D.Double(state.getCursor(), 1090 dataArea.getY(), cursor - state.getCursor(), 1091 dataArea.getHeight()); 1092 } 1093 else if (edge.equals(RectangleEdge.RIGHT)) { 1094 hotspot = new Rectangle2D.Double(cursor, dataArea.getY(), 1095 state.getCursor() - cursor, dataArea.getHeight()); 1096 } 1097 EntityCollection e = plotState.getOwner().getEntityCollection(); 1098 if (e != null) { 1099 e.add(new AxisEntity(hotspot, this)); 1100 } 1101 } 1102 1103 /** 1104 * Registers an object for notification of changes to the axis. 1105 * 1106 * @param listener the object that is being registered. 1107 * 1108 * @see #removeChangeListener(AxisChangeListener) 1109 */ 1110 public void addChangeListener(AxisChangeListener listener) { 1111 this.listenerList.add(AxisChangeListener.class, listener); 1112 } 1113 1114 /** 1115 * Deregisters an object for notification of changes to the axis. 1116 * 1117 * @param listener the object to deregister. 1118 * 1119 * @see #addChangeListener(AxisChangeListener) 1120 */ 1121 public void removeChangeListener(AxisChangeListener listener) { 1122 this.listenerList.remove(AxisChangeListener.class, listener); 1123 } 1124 1125 /** 1126 * Returns <code>true</code> if the specified object is registered with 1127 * the dataset as a listener. Most applications won't need to call this 1128 * method, it exists mainly for use by unit testing code. 1129 * 1130 * @param listener the listener. 1131 * 1132 * @return A boolean. 1133 */ 1134 public boolean hasListener(EventListener listener) { 1135 List list = Arrays.asList(this.listenerList.getListenerList()); 1136 return list.contains(listener); 1137 } 1138 1139 /** 1140 * Notifies all registered listeners that the axis has changed. 1141 * The AxisChangeEvent provides information about the change. 1142 * 1143 * @param event information about the change to the axis. 1144 */ 1145 protected void notifyListeners(AxisChangeEvent event) { 1146 Object[] listeners = this.listenerList.getListenerList(); 1147 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1148 if (listeners[i] == AxisChangeListener.class) { 1149 ((AxisChangeListener) listeners[i + 1]).axisChanged(event); 1150 } 1151 } 1152 } 1153 1154 /** 1155 * Sends an {@link AxisChangeEvent} to all registered listeners. 1156 * 1157 * @since 1.0.12 1158 */ 1159 protected void fireChangeEvent() { 1160 notifyListeners(new AxisChangeEvent(this)); 1161 } 1162 1163 /** 1164 * Returns a rectangle that encloses the axis label. This is typically 1165 * used for layout purposes (it gives the maximum dimensions of the label). 1166 * 1167 * @param g2 the graphics device. 1168 * @param edge the edge of the plot area along which the axis is measuring. 1169 * 1170 * @return The enclosing rectangle. 1171 */ 1172 protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) { 1173 1174 Rectangle2D result = new Rectangle2D.Double(); 1175 String axisLabel = getLabel(); 1176 if (axisLabel != null && !axisLabel.equals("")) { 1177 FontMetrics fm = g2.getFontMetrics(getLabelFont()); 1178 Rectangle2D bounds = TextUtilities.getTextBounds(axisLabel, g2, fm); 1179 RectangleInsets insets = getLabelInsets(); 1180 bounds = insets.createOutsetRectangle(bounds); 1181 double angle = getLabelAngle(); 1182 if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) { 1183 angle = angle - Math.PI / 2.0; 1184 } 1185 double x = bounds.getCenterX(); 1186 double y = bounds.getCenterY(); 1187 AffineTransform transformer 1188 = AffineTransform.getRotateInstance(angle, x, y); 1189 Shape labelBounds = transformer.createTransformedShape(bounds); 1190 result = labelBounds.getBounds2D(); 1191 } 1192 1193 return result; 1194 1195 } 1196 1197 /** 1198 * Draws the axis label. 1199 * 1200 * @param label the label text. 1201 * @param g2 the graphics device. 1202 * @param plotArea the plot area. 1203 * @param dataArea the area inside the axes. 1204 * @param edge the location of the axis. 1205 * @param state the axis state (<code>null</code> not permitted). 1206 * 1207 * @return Information about the axis. 1208 */ 1209 protected AxisState drawLabel(String label, Graphics2D g2, 1210 Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, 1211 AxisState state) { 1212 1213 // it is unlikely that 'state' will be null, but check anyway... 1214 if (state == null) { 1215 throw new IllegalArgumentException("Null 'state' argument."); 1216 } 1217 1218 if ((label == null) || (label.equals(""))) { 1219 return state; 1220 } 1221 1222 Font font = getLabelFont(); 1223 RectangleInsets insets = getLabelInsets(); 1224 g2.setFont(font); 1225 g2.setPaint(getLabelPaint()); 1226 FontMetrics fm = g2.getFontMetrics(); 1227 Rectangle2D labelBounds = TextUtilities.getTextBounds(label, g2, fm); 1228 1229 if (edge == RectangleEdge.TOP) { 1230 AffineTransform t = AffineTransform.getRotateInstance( 1231 getLabelAngle(), labelBounds.getCenterX(), 1232 labelBounds.getCenterY()); 1233 Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); 1234 labelBounds = rotatedLabelBounds.getBounds2D(); 1235 double labelx = dataArea.getCenterX(); 1236 double labely = state.getCursor() - insets.getBottom() 1237 - labelBounds.getHeight() / 2.0; 1238 TextUtilities.drawRotatedString(label, g2, (float) labelx, 1239 (float) labely, TextAnchor.CENTER, getLabelAngle(), 1240 TextAnchor.CENTER); 1241 state.cursorUp(insets.getTop() + labelBounds.getHeight() 1242 + insets.getBottom()); 1243 } 1244 else if (edge == RectangleEdge.BOTTOM) { 1245 AffineTransform t = AffineTransform.getRotateInstance( 1246 getLabelAngle(), labelBounds.getCenterX(), 1247 labelBounds.getCenterY()); 1248 Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); 1249 labelBounds = rotatedLabelBounds.getBounds2D(); 1250 double labelx = dataArea.getCenterX(); 1251 double labely = state.getCursor() 1252 + insets.getTop() + labelBounds.getHeight() / 2.0; 1253 TextUtilities.drawRotatedString(label, g2, (float) labelx, 1254 (float) labely, TextAnchor.CENTER, getLabelAngle(), 1255 TextAnchor.CENTER); 1256 state.cursorDown(insets.getTop() + labelBounds.getHeight() 1257 + insets.getBottom()); 1258 } 1259 else if (edge == RectangleEdge.LEFT) { 1260 AffineTransform t = AffineTransform.getRotateInstance( 1261 getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(), 1262 labelBounds.getCenterY()); 1263 Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); 1264 labelBounds = rotatedLabelBounds.getBounds2D(); 1265 double labelx = state.getCursor() 1266 - insets.getRight() - labelBounds.getWidth() / 2.0; 1267 double labely = dataArea.getCenterY(); 1268 TextUtilities.drawRotatedString(label, g2, (float) labelx, 1269 (float) labely, TextAnchor.CENTER, 1270 getLabelAngle() - Math.PI / 2.0, TextAnchor.CENTER); 1271 state.cursorLeft(insets.getLeft() + labelBounds.getWidth() 1272 + insets.getRight()); 1273 } 1274 else if (edge == RectangleEdge.RIGHT) { 1275 1276 AffineTransform t = AffineTransform.getRotateInstance( 1277 getLabelAngle() + Math.PI / 2.0, 1278 labelBounds.getCenterX(), labelBounds.getCenterY()); 1279 Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); 1280 labelBounds = rotatedLabelBounds.getBounds2D(); 1281 double labelx = state.getCursor() 1282 + insets.getLeft() + labelBounds.getWidth() / 2.0; 1283 double labely = dataArea.getY() + dataArea.getHeight() / 2.0; 1284 TextUtilities.drawRotatedString(label, g2, (float) labelx, 1285 (float) labely, TextAnchor.CENTER, 1286 getLabelAngle() + Math.PI / 2.0, TextAnchor.CENTER); 1287 state.cursorRight(insets.getLeft() + labelBounds.getWidth() 1288 + insets.getRight()); 1289 1290 } 1291 1292 return state; 1293 1294 } 1295 1296 /** 1297 * Draws an axis line at the current cursor position and edge. 1298 * 1299 * @param g2 the graphics device. 1300 * @param cursor the cursor position. 1301 * @param dataArea the data area. 1302 * @param edge the edge. 1303 */ 1304 protected void drawAxisLine(Graphics2D g2, double cursor, 1305 Rectangle2D dataArea, RectangleEdge edge) { 1306 1307 Line2D axisLine = null; 1308 if (edge == RectangleEdge.TOP) { 1309 axisLine = new Line2D.Double(dataArea.getX(), cursor, 1310 dataArea.getMaxX(), cursor); 1311 } 1312 else if (edge == RectangleEdge.BOTTOM) { 1313 axisLine = new Line2D.Double(dataArea.getX(), cursor, 1314 dataArea.getMaxX(), cursor); 1315 } 1316 else if (edge == RectangleEdge.LEFT) { 1317 axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor, 1318 dataArea.getMaxY()); 1319 } 1320 else if (edge == RectangleEdge.RIGHT) { 1321 axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor, 1322 dataArea.getMaxY()); 1323 } 1324 g2.setPaint(this.axisLinePaint); 1325 g2.setStroke(this.axisLineStroke); 1326 g2.draw(axisLine); 1327 1328 } 1329 1330 /** 1331 * Returns a clone of the axis. 1332 * 1333 * @return A clone. 1334 * 1335 * @throws CloneNotSupportedException if some component of the axis does 1336 * not support cloning. 1337 */ 1338 public Object clone() throws CloneNotSupportedException { 1339 Axis clone = (Axis) super.clone(); 1340 // It's up to the plot which clones up to restore the correct references 1341 clone.plot = null; 1342 clone.listenerList = new EventListenerList(); 1343 return clone; 1344 } 1345 1346 /** 1347 * Tests this axis for equality with another object. 1348 * 1349 * @param obj the object (<code>null</code> permitted). 1350 * 1351 * @return <code>true</code> or <code>false</code>. 1352 */ 1353 public boolean equals(Object obj) { 1354 if (obj == this) { 1355 return true; 1356 } 1357 if (!(obj instanceof Axis)) { 1358 return false; 1359 } 1360 Axis that = (Axis) obj; 1361 if (this.visible != that.visible) { 1362 return false; 1363 } 1364 if (!ObjectUtilities.equal(this.label, that.label)) { 1365 return false; 1366 } 1367 if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) { 1368 return false; 1369 } 1370 if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) { 1371 return false; 1372 } 1373 if (!ObjectUtilities.equal(this.labelInsets, that.labelInsets)) { 1374 return false; 1375 } 1376 if (this.labelAngle != that.labelAngle) { 1377 return false; 1378 } 1379 if (this.axisLineVisible != that.axisLineVisible) { 1380 return false; 1381 } 1382 if (!ObjectUtilities.equal(this.axisLineStroke, that.axisLineStroke)) { 1383 return false; 1384 } 1385 if (!PaintUtilities.equal(this.axisLinePaint, that.axisLinePaint)) { 1386 return false; 1387 } 1388 if (this.tickLabelsVisible != that.tickLabelsVisible) { 1389 return false; 1390 } 1391 if (!ObjectUtilities.equal(this.tickLabelFont, that.tickLabelFont)) { 1392 return false; 1393 } 1394 if (!PaintUtilities.equal(this.tickLabelPaint, that.tickLabelPaint)) { 1395 return false; 1396 } 1397 if (!ObjectUtilities.equal( 1398 this.tickLabelInsets, that.tickLabelInsets 1399 )) { 1400 return false; 1401 } 1402 if (this.tickMarksVisible != that.tickMarksVisible) { 1403 return false; 1404 } 1405 if (this.tickMarkInsideLength != that.tickMarkInsideLength) { 1406 return false; 1407 } 1408 if (this.tickMarkOutsideLength != that.tickMarkOutsideLength) { 1409 return false; 1410 } 1411 if (!PaintUtilities.equal(this.tickMarkPaint, that.tickMarkPaint)) { 1412 return false; 1413 } 1414 if (!ObjectUtilities.equal(this.tickMarkStroke, that.tickMarkStroke)) { 1415 return false; 1416 } 1417 if (this.minorTickMarksVisible != that.minorTickMarksVisible) { 1418 return false; 1419 } 1420 if (this.minorTickMarkInsideLength != that.minorTickMarkInsideLength) { 1421 return false; 1422 } 1423 if (this.minorTickMarkOutsideLength 1424 != that.minorTickMarkOutsideLength) { 1425 return false; 1426 } 1427 if (this.fixedDimension != that.fixedDimension) { 1428 return false; 1429 } 1430 return true; 1431 } 1432 1433 /** 1434 * Provides serialization support. 1435 * 1436 * @param stream the output stream. 1437 * 1438 * @throws IOException if there is an I/O error. 1439 */ 1440 private void writeObject(ObjectOutputStream stream) throws IOException { 1441 stream.defaultWriteObject(); 1442 SerialUtilities.writePaint(this.labelPaint, stream); 1443 SerialUtilities.writePaint(this.tickLabelPaint, stream); 1444 SerialUtilities.writeStroke(this.axisLineStroke, stream); 1445 SerialUtilities.writePaint(this.axisLinePaint, stream); 1446 SerialUtilities.writeStroke(this.tickMarkStroke, stream); 1447 SerialUtilities.writePaint(this.tickMarkPaint, stream); 1448 } 1449 1450 /** 1451 * Provides serialization support. 1452 * 1453 * @param stream the input stream. 1454 * 1455 * @throws IOException if there is an I/O error. 1456 * @throws ClassNotFoundException if there is a classpath problem. 1457 */ 1458 private void readObject(ObjectInputStream stream) 1459 throws IOException, ClassNotFoundException { 1460 stream.defaultReadObject(); 1461 this.labelPaint = SerialUtilities.readPaint(stream); 1462 this.tickLabelPaint = SerialUtilities.readPaint(stream); 1463 this.axisLineStroke = SerialUtilities.readStroke(stream); 1464 this.axisLinePaint = SerialUtilities.readPaint(stream); 1465 this.tickMarkStroke = SerialUtilities.readStroke(stream); 1466 this.tickMarkPaint = SerialUtilities.readPaint(stream); 1467 this.listenerList = new EventListenerList(); 1468 } 1469 1470}