Source for org.jfree.chart.axis.Axis

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