0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ---------
0028: * Axis.java
0029: * ---------
0030: * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Bill Kelemen; Nicolas Brodu
0034: *
0035: * $Id: Axis.java,v 1.11.2.4 2006/08/23 10:24:26 mungady Exp $
0036: *
0037: * Changes (from 21-Aug-2001)
0038: * --------------------------
0039: * 21-Aug-2001 : Added standard header, fixed DOS encoding problem (DG);
0040: * 18-Sep-2001 : Updated header (DG);
0041: * 07-Nov-2001 : Allow null axis labels (DG);
0042: * : Added default font values (DG);
0043: * 13-Nov-2001 : Modified the setPlot() method to check compatibility between
0044: * the axis and the plot (DG);
0045: * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG);
0046: * 06-Dec-2001 : Allow null in setPlot() method (BK);
0047: * 06-Mar-2002 : Added AxisConstants interface (DG);
0048: * 23-Apr-2002 : Added a visible property. Moved drawVerticalString to
0049: * RefineryUtilities. Added fixedDimension property for use in
0050: * combined plots (DG);
0051: * 25-Jun-2002 : Removed unnecessary imports (DG);
0052: * 05-Sep-2002 : Added attribute for tick mark paint (DG);
0053: * 18-Sep-2002 : Fixed errors reported by Checkstyle (DG);
0054: * 07-Nov-2002 : Added attributes to control the inside and outside length of
0055: * the tick marks (DG);
0056: * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
0057: * 18-Nov-2002 : Added axis location to refreshTicks() parameters (DG);
0058: * 15-Jan-2003 : Removed monolithic constructor (DG);
0059: * 17-Jan-2003 : Moved plot classes to separate package (DG);
0060: * 26-Mar-2003 : Implemented Serializable (DG);
0061: * 03-Jul-2003 : Modified reserveSpace method (DG);
0062: * 13-Aug-2003 : Implemented Cloneable (DG);
0063: * 11-Sep-2003 : Took care of listeners while cloning (NB);
0064: * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
0065: * 06-Nov-2003 : Modified refreshTicks() signature (DG);
0066: * 06-Jan-2004 : Added axis line attributes (DG);
0067: * 16-Mar-2004 : Added plot state to draw() method (DG);
0068: * 07-Apr-2004 : Modified text bounds calculation (DG);
0069: * 18-May-2004 : Eliminated AxisConstants.java (DG);
0070: * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities -->
0071: * TextUtilities (DG);
0072: * 04-Oct-2004 : Modified getLabelEnclosure() method to treat an empty String
0073: * the same way as a null string - see bug 1026521 (DG);
0074: * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG);
0075: * 26-Apr-2005 : Removed LOGGER (DG);
0076: * 01-Jun-2005 : Added hasListener() method for unit testing (DG);
0077: * 08-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
0078: * ------------- JFREECHART 1.0.0 ---------------------------------------------
0079: * 22-Aug-2006 : API doc updates (DG);
0080: *
0081: */
0082:
0083: package org.jfree.chart.axis;
0084:
0085: import java.awt.BasicStroke;
0086: import java.awt.Color;
0087: import java.awt.Font;
0088: import java.awt.FontMetrics;
0089: import java.awt.Graphics2D;
0090: import java.awt.Paint;
0091: import java.awt.Shape;
0092: import java.awt.Stroke;
0093: import java.awt.geom.AffineTransform;
0094: import java.awt.geom.Line2D;
0095: import java.awt.geom.Rectangle2D;
0096: import java.io.IOException;
0097: import java.io.ObjectInputStream;
0098: import java.io.ObjectOutputStream;
0099: import java.io.Serializable;
0100: import java.util.Arrays;
0101: import java.util.EventListener;
0102: import java.util.List;
0103:
0104: import javax.swing.event.EventListenerList;
0105:
0106: import org.jfree.chart.event.AxisChangeEvent;
0107: import org.jfree.chart.event.AxisChangeListener;
0108: import org.jfree.chart.plot.Plot;
0109: import org.jfree.chart.plot.PlotRenderingInfo;
0110: import org.jfree.io.SerialUtilities;
0111: import org.jfree.text.TextUtilities;
0112: import org.jfree.ui.RectangleEdge;
0113: import org.jfree.ui.RectangleInsets;
0114: import org.jfree.ui.TextAnchor;
0115: import org.jfree.util.ObjectUtilities;
0116: import org.jfree.util.PaintUtilities;
0117:
0118: /**
0119: * The base class for all axes in JFreeChart. Subclasses are divided into
0120: * those that display values ({@link ValueAxis}) and those that display
0121: * categories ({@link CategoryAxis}).
0122: */
0123: public abstract class Axis implements Cloneable, Serializable {
0124:
0125: /** For serialization. */
0126: private static final long serialVersionUID = 7719289504573298271L;
0127:
0128: /** The default axis visibility. */
0129: public static final boolean DEFAULT_AXIS_VISIBLE = true;
0130:
0131: /** The default axis label font. */
0132: public static final Font DEFAULT_AXIS_LABEL_FONT = new Font(
0133: "SansSerif", Font.PLAIN, 12);
0134:
0135: /** The default axis label paint. */
0136: public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.black;
0137:
0138: /** The default axis label insets. */
0139: public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS = new RectangleInsets(
0140: 3.0, 3.0, 3.0, 3.0);
0141:
0142: /** The default axis line paint. */
0143: public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.gray;
0144:
0145: /** The default axis line stroke. */
0146: public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(
0147: 1.0f);
0148:
0149: /** The default tick labels visibility. */
0150: public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true;
0151:
0152: /** The default tick label font. */
0153: public static final Font DEFAULT_TICK_LABEL_FONT = new Font(
0154: "SansSerif", Font.PLAIN, 10);
0155:
0156: /** The default tick label paint. */
0157: public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.black;
0158:
0159: /** The default tick label insets. */
0160: public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS = new RectangleInsets(
0161: 2.0, 4.0, 2.0, 4.0);
0162:
0163: /** The default tick marks visible. */
0164: public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true;
0165:
0166: /** The default tick stroke. */
0167: public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(
0168: 1);
0169:
0170: /** The default tick paint. */
0171: public static final Paint DEFAULT_TICK_MARK_PAINT = Color.gray;
0172:
0173: /** The default tick mark inside length. */
0174: public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f;
0175:
0176: /** The default tick mark outside length. */
0177: public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f;
0178:
0179: /** A flag indicating whether or not the axis is visible. */
0180: private boolean visible;
0181:
0182: /** The label for the axis. */
0183: private String label;
0184:
0185: /** The font for displaying the axis label. */
0186: private Font labelFont;
0187:
0188: /** The paint for drawing the axis label. */
0189: private transient Paint labelPaint;
0190:
0191: /** The insets for the axis label. */
0192: private RectangleInsets labelInsets;
0193:
0194: /** The label angle. */
0195: private double labelAngle;
0196:
0197: /** A flag that controls whether or not the axis line is visible. */
0198: private boolean axisLineVisible;
0199:
0200: /** The stroke used for the axis line. */
0201: private transient Stroke axisLineStroke;
0202:
0203: /** The paint used for the axis line. */
0204: private transient Paint axisLinePaint;
0205:
0206: /**
0207: * A flag that indicates whether or not tick labels are visible for the
0208: * axis.
0209: */
0210: private boolean tickLabelsVisible;
0211:
0212: /** The font used to display the tick labels. */
0213: private Font tickLabelFont;
0214:
0215: /** The color used to display the tick labels. */
0216: private transient Paint tickLabelPaint;
0217:
0218: /** The blank space around each tick label. */
0219: private RectangleInsets tickLabelInsets;
0220:
0221: /**
0222: * A flag that indicates whether or not tick marks are visible for the
0223: * axis.
0224: */
0225: private boolean tickMarksVisible;
0226:
0227: /** The length of the tick mark inside the data area (zero permitted). */
0228: private float tickMarkInsideLength;
0229:
0230: /** The length of the tick mark outside the data area (zero permitted). */
0231: private float tickMarkOutsideLength;
0232:
0233: /** The stroke used to draw tick marks. */
0234: private transient Stroke tickMarkStroke;
0235:
0236: /** The paint used to draw tick marks. */
0237: private transient Paint tickMarkPaint;
0238:
0239: /** The fixed (horizontal or vertical) dimension for the axis. */
0240: private double fixedDimension;
0241:
0242: /**
0243: * A reference back to the plot that the axis is assigned to (can be
0244: * <code>null</code>).
0245: */
0246: private transient Plot plot;
0247:
0248: /** Storage for registered listeners. */
0249: private transient EventListenerList listenerList;
0250:
0251: /**
0252: * Constructs an axis, using default values where necessary.
0253: *
0254: * @param label the axis label (<code>null</code> permitted).
0255: */
0256: protected Axis(String label) {
0257:
0258: this .label = label;
0259: this .visible = DEFAULT_AXIS_VISIBLE;
0260: this .labelFont = DEFAULT_AXIS_LABEL_FONT;
0261: this .labelPaint = DEFAULT_AXIS_LABEL_PAINT;
0262: this .labelInsets = DEFAULT_AXIS_LABEL_INSETS;
0263: this .labelAngle = 0.0;
0264:
0265: this .axisLineVisible = true;
0266: this .axisLinePaint = DEFAULT_AXIS_LINE_PAINT;
0267: this .axisLineStroke = DEFAULT_AXIS_LINE_STROKE;
0268:
0269: this .tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE;
0270: this .tickLabelFont = DEFAULT_TICK_LABEL_FONT;
0271: this .tickLabelPaint = DEFAULT_TICK_LABEL_PAINT;
0272: this .tickLabelInsets = DEFAULT_TICK_LABEL_INSETS;
0273:
0274: this .tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE;
0275: this .tickMarkStroke = DEFAULT_TICK_MARK_STROKE;
0276: this .tickMarkPaint = DEFAULT_TICK_MARK_PAINT;
0277: this .tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH;
0278: this .tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH;
0279:
0280: this .plot = null;
0281:
0282: this .listenerList = new EventListenerList();
0283:
0284: }
0285:
0286: /**
0287: * Returns <code>true</code> if the axis is visible, and
0288: * <code>false</code> otherwise.
0289: *
0290: * @return A boolean.
0291: *
0292: * @see #setVisible(boolean)
0293: */
0294: public boolean isVisible() {
0295: return this .visible;
0296: }
0297:
0298: /**
0299: * Sets a flag that controls whether or not the axis is visible and sends
0300: * an {@link AxisChangeEvent} to all registered listeners.
0301: *
0302: * @param flag the flag.
0303: *
0304: * @see #isVisible()
0305: */
0306: public void setVisible(boolean flag) {
0307: if (flag != this .visible) {
0308: this .visible = flag;
0309: notifyListeners(new AxisChangeEvent(this ));
0310: }
0311: }
0312:
0313: /**
0314: * Returns the label for the axis.
0315: *
0316: * @return The label for the axis (<code>null</code> possible).
0317: *
0318: * @see #getLabelFont()
0319: * @see #getLabelPaint()
0320: * @see #setLabel(String)
0321: */
0322: public String getLabel() {
0323: return this .label;
0324: }
0325:
0326: /**
0327: * Sets the label for the axis and sends an {@link AxisChangeEvent} to all
0328: * registered listeners.
0329: *
0330: * @param label the new label (<code>null</code> permitted).
0331: *
0332: * @see #getLabel()
0333: * @see #setLabelFont(Font)
0334: * @see #setLabelPaint(Paint)
0335: */
0336: public void setLabel(String label) {
0337:
0338: String existing = this .label;
0339: if (existing != null) {
0340: if (!existing.equals(label)) {
0341: this .label = label;
0342: notifyListeners(new AxisChangeEvent(this ));
0343: }
0344: } else {
0345: if (label != null) {
0346: this .label = label;
0347: notifyListeners(new AxisChangeEvent(this ));
0348: }
0349: }
0350:
0351: }
0352:
0353: /**
0354: * Returns the font for the axis label.
0355: *
0356: * @return The font (never <code>null</code>).
0357: *
0358: * @see #setLabelFont(Font)
0359: */
0360: public Font getLabelFont() {
0361: return this .labelFont;
0362: }
0363:
0364: /**
0365: * Sets the font for the axis label and sends an {@link AxisChangeEvent}
0366: * to all registered listeners.
0367: *
0368: * @param font the font (<code>null</code> not permitted).
0369: *
0370: * @see #getLabelFont()
0371: */
0372: public void setLabelFont(Font font) {
0373: if (font == null) {
0374: throw new IllegalArgumentException("Null 'font' argument.");
0375: }
0376: if (!this .labelFont.equals(font)) {
0377: this .labelFont = font;
0378: notifyListeners(new AxisChangeEvent(this ));
0379: }
0380: }
0381:
0382: /**
0383: * Returns the color/shade used to draw the axis label.
0384: *
0385: * @return The paint (never <code>null</code>).
0386: *
0387: * @see #setLabelPaint(Paint)
0388: */
0389: public Paint getLabelPaint() {
0390: return this .labelPaint;
0391: }
0392:
0393: /**
0394: * Sets the paint used to draw the axis label and sends an
0395: * {@link AxisChangeEvent} to all registered listeners.
0396: *
0397: * @param paint the paint (<code>null</code> not permitted).
0398: *
0399: * @see #getLabelPaint()
0400: */
0401: public void setLabelPaint(Paint paint) {
0402: if (paint == null) {
0403: throw new IllegalArgumentException("Null 'paint' argument.");
0404: }
0405: this .labelPaint = paint;
0406: notifyListeners(new AxisChangeEvent(this ));
0407: }
0408:
0409: /**
0410: * Returns the insets for the label (that is, the amount of blank space
0411: * that should be left around the label).
0412: *
0413: * @return The label insets (never <code>null</code>).
0414: *
0415: * @see #setLabelInsets(RectangleInsets)
0416: */
0417: public RectangleInsets getLabelInsets() {
0418: return this .labelInsets;
0419: }
0420:
0421: /**
0422: * Sets the insets for the axis label, and sends an {@link AxisChangeEvent}
0423: * to all registered listeners.
0424: *
0425: * @param insets the insets (<code>null</code> not permitted).
0426: *
0427: * @see #getLabelInsets()
0428: */
0429: public void setLabelInsets(RectangleInsets insets) {
0430: if (insets == null) {
0431: throw new IllegalArgumentException(
0432: "Null 'insets' argument.");
0433: }
0434: if (!insets.equals(this .labelInsets)) {
0435: this .labelInsets = insets;
0436: notifyListeners(new AxisChangeEvent(this ));
0437: }
0438: }
0439:
0440: /**
0441: * Returns the angle of the axis label.
0442: *
0443: * @return The angle (in radians).
0444: *
0445: * @see #setLabelAngle(double)
0446: */
0447: public double getLabelAngle() {
0448: return this .labelAngle;
0449: }
0450:
0451: /**
0452: * Sets the angle for the label and sends an {@link AxisChangeEvent} to all
0453: * registered listeners.
0454: *
0455: * @param angle the angle (in radians).
0456: *
0457: * @see #getLabelAngle()
0458: */
0459: public void setLabelAngle(double angle) {
0460: this .labelAngle = angle;
0461: notifyListeners(new AxisChangeEvent(this ));
0462: }
0463:
0464: /**
0465: * A flag that controls whether or not the axis line is drawn.
0466: *
0467: * @return A boolean.
0468: *
0469: * @see #getAxisLinePaint()
0470: * @see #getAxisLineStroke()
0471: * @see #setAxisLineVisible(boolean)
0472: */
0473: public boolean isAxisLineVisible() {
0474: return this .axisLineVisible;
0475: }
0476:
0477: /**
0478: * Sets a flag that controls whether or not the axis line is visible and
0479: * sends an {@link AxisChangeEvent} to all registered listeners.
0480: *
0481: * @param visible the flag.
0482: *
0483: * @see #isAxisLineVisible()
0484: * @see #setAxisLinePaint(Paint)
0485: * @see #setAxisLineStroke(Stroke)
0486: */
0487: public void setAxisLineVisible(boolean visible) {
0488: this .axisLineVisible = visible;
0489: notifyListeners(new AxisChangeEvent(this ));
0490: }
0491:
0492: /**
0493: * Returns the paint used to draw the axis line.
0494: *
0495: * @return The paint (never <code>null</code>).
0496: *
0497: * @see #setAxisLinePaint(Paint)
0498: */
0499: public Paint getAxisLinePaint() {
0500: return this .axisLinePaint;
0501: }
0502:
0503: /**
0504: * Sets the paint used to draw the axis line and sends an
0505: * {@link AxisChangeEvent} to all registered listeners.
0506: *
0507: * @param paint the paint (<code>null</code> not permitted).
0508: *
0509: * @see #getAxisLinePaint()
0510: */
0511: public void setAxisLinePaint(Paint paint) {
0512: if (paint == null) {
0513: throw new IllegalArgumentException("Null 'paint' argument.");
0514: }
0515: this .axisLinePaint = paint;
0516: notifyListeners(new AxisChangeEvent(this ));
0517: }
0518:
0519: /**
0520: * Returns the stroke used to draw the axis line.
0521: *
0522: * @return The stroke (never <code>null</code>).
0523: *
0524: * @see #setAxisLineStroke(Stroke)
0525: */
0526: public Stroke getAxisLineStroke() {
0527: return this .axisLineStroke;
0528: }
0529:
0530: /**
0531: * Sets the stroke used to draw the axis line and sends an
0532: * {@link AxisChangeEvent} to all registered listeners.
0533: *
0534: * @param stroke the stroke (<code>null</code> not permitted).
0535: *
0536: * @see #getAxisLineStroke()
0537: */
0538: public void setAxisLineStroke(Stroke stroke) {
0539: if (stroke == null) {
0540: throw new IllegalArgumentException(
0541: "Null 'stroke' argument.");
0542: }
0543: this .axisLineStroke = stroke;
0544: notifyListeners(new AxisChangeEvent(this ));
0545: }
0546:
0547: /**
0548: * Returns a flag indicating whether or not the tick labels are visible.
0549: *
0550: * @return The flag.
0551: *
0552: * @see #getTickLabelFont()
0553: * @see #getTickLabelPaint()
0554: * @see #setTickLabelsVisible(boolean)
0555: */
0556: public boolean isTickLabelsVisible() {
0557: return this .tickLabelsVisible;
0558: }
0559:
0560: /**
0561: * Sets the flag that determines whether or not the tick labels are
0562: * visible and sends an {@link AxisChangeEvent} to all registered
0563: * listeners.
0564: *
0565: * @param flag the flag.
0566: *
0567: * @see #isTickLabelsVisible()
0568: * @see #setTickLabelFont(Font)
0569: * @see #setTickLabelPaint(Paint)
0570: */
0571: public void setTickLabelsVisible(boolean flag) {
0572:
0573: if (flag != this .tickLabelsVisible) {
0574: this .tickLabelsVisible = flag;
0575: notifyListeners(new AxisChangeEvent(this ));
0576: }
0577:
0578: }
0579:
0580: /**
0581: * Returns the font used for the tick labels (if showing).
0582: *
0583: * @return The font (never <code>null</code>).
0584: *
0585: * @see #setTickLabelFont(Font)
0586: */
0587: public Font getTickLabelFont() {
0588: return this .tickLabelFont;
0589: }
0590:
0591: /**
0592: * Sets the font for the tick labels and sends an {@link AxisChangeEvent}
0593: * to all registered listeners.
0594: *
0595: * @param font the font (<code>null</code> not allowed).
0596: *
0597: * @see #getTickLabelFont()
0598: */
0599: public void setTickLabelFont(Font font) {
0600:
0601: if (font == null) {
0602: throw new IllegalArgumentException("Null 'font' argument.");
0603: }
0604:
0605: if (!this .tickLabelFont.equals(font)) {
0606: this .tickLabelFont = font;
0607: notifyListeners(new AxisChangeEvent(this ));
0608: }
0609:
0610: }
0611:
0612: /**
0613: * Returns the color/shade used for the tick labels.
0614: *
0615: * @return The paint used for the tick labels.
0616: *
0617: * @see #setTickLabelPaint(Paint)
0618: */
0619: public Paint getTickLabelPaint() {
0620: return this .tickLabelPaint;
0621: }
0622:
0623: /**
0624: * Sets the paint used to draw tick labels (if they are showing) and
0625: * sends an {@link AxisChangeEvent} to all registered listeners.
0626: *
0627: * @param paint the paint (<code>null</code> not permitted).
0628: *
0629: * @see #getTickLabelPaint()
0630: */
0631: public void setTickLabelPaint(Paint paint) {
0632: if (paint == null) {
0633: throw new IllegalArgumentException("Null 'paint' argument.");
0634: }
0635: this .tickLabelPaint = paint;
0636: notifyListeners(new AxisChangeEvent(this ));
0637: }
0638:
0639: /**
0640: * Returns the insets for the tick labels.
0641: *
0642: * @return The insets (never <code>null</code>).
0643: *
0644: * @see #setTickLabelInsets(RectangleInsets)
0645: */
0646: public RectangleInsets getTickLabelInsets() {
0647: return this .tickLabelInsets;
0648: }
0649:
0650: /**
0651: * Sets the insets for the tick labels and sends an {@link AxisChangeEvent}
0652: * to all registered listeners.
0653: *
0654: * @param insets the insets (<code>null</code> not permitted).
0655: *
0656: * @see #getTickLabelInsets()
0657: */
0658: public void setTickLabelInsets(RectangleInsets insets) {
0659: if (insets == null) {
0660: throw new IllegalArgumentException(
0661: "Null 'insets' argument.");
0662: }
0663: if (!this .tickLabelInsets.equals(insets)) {
0664: this .tickLabelInsets = insets;
0665: notifyListeners(new AxisChangeEvent(this ));
0666: }
0667: }
0668:
0669: /**
0670: * Returns the flag that indicates whether or not the tick marks are
0671: * showing.
0672: *
0673: * @return The flag that indicates whether or not the tick marks are
0674: * showing.
0675: *
0676: * @see #setTickMarksVisible(boolean)
0677: */
0678: public boolean isTickMarksVisible() {
0679: return this .tickMarksVisible;
0680: }
0681:
0682: /**
0683: * Sets the flag that indicates whether or not the tick marks are showing
0684: * and sends an {@link AxisChangeEvent} to all registered listeners.
0685: *
0686: * @param flag the flag.
0687: *
0688: * @see #isTickMarksVisible()
0689: */
0690: public void setTickMarksVisible(boolean flag) {
0691: if (flag != this .tickMarksVisible) {
0692: this .tickMarksVisible = flag;
0693: notifyListeners(new AxisChangeEvent(this ));
0694: }
0695: }
0696:
0697: /**
0698: * Returns the inside length of the tick marks.
0699: *
0700: * @return The length.
0701: *
0702: * @see #getTickMarkOutsideLength()
0703: * @see #setTickMarkInsideLength(float)
0704: */
0705: public float getTickMarkInsideLength() {
0706: return this .tickMarkInsideLength;
0707: }
0708:
0709: /**
0710: * Sets the inside length of the tick marks and sends
0711: * an {@link AxisChangeEvent} to all registered listeners.
0712: *
0713: * @param length the new length.
0714: *
0715: * @see #getTickMarkInsideLength()
0716: */
0717: public void setTickMarkInsideLength(float length) {
0718: this .tickMarkInsideLength = length;
0719: notifyListeners(new AxisChangeEvent(this ));
0720: }
0721:
0722: /**
0723: * Returns the outside length of the tick marks.
0724: *
0725: * @return The length.
0726: *
0727: * @see #getTickMarkInsideLength()
0728: * @see #setTickMarkOutsideLength(float)
0729: */
0730: public float getTickMarkOutsideLength() {
0731: return this .tickMarkOutsideLength;
0732: }
0733:
0734: /**
0735: * Sets the outside length of the tick marks and sends
0736: * an {@link AxisChangeEvent} to all registered listeners.
0737: *
0738: * @param length the new length.
0739: *
0740: * @see #getTickMarkInsideLength()
0741: */
0742: public void setTickMarkOutsideLength(float length) {
0743: this .tickMarkOutsideLength = length;
0744: notifyListeners(new AxisChangeEvent(this ));
0745: }
0746:
0747: /**
0748: * Returns the stroke used to draw tick marks.
0749: *
0750: * @return The stroke (never <code>null</code>).
0751: *
0752: * @see #setTickMarkStroke(Stroke)
0753: */
0754: public Stroke getTickMarkStroke() {
0755: return this .tickMarkStroke;
0756: }
0757:
0758: /**
0759: * Sets the stroke used to draw tick marks and sends
0760: * an {@link AxisChangeEvent} to all registered listeners.
0761: *
0762: * @param stroke the stroke (<code>null</code> not permitted).
0763: *
0764: * @see #getTickMarkStroke()
0765: */
0766: public void setTickMarkStroke(Stroke stroke) {
0767: if (stroke == null) {
0768: throw new IllegalArgumentException(
0769: "Null 'stroke' argument.");
0770: }
0771: if (!this .tickMarkStroke.equals(stroke)) {
0772: this .tickMarkStroke = stroke;
0773: notifyListeners(new AxisChangeEvent(this ));
0774: }
0775: }
0776:
0777: /**
0778: * Returns the paint used to draw tick marks (if they are showing).
0779: *
0780: * @return The paint (never <code>null</code>).
0781: *
0782: * @see #setTickMarkPaint(Paint)
0783: */
0784: public Paint getTickMarkPaint() {
0785: return this .tickMarkPaint;
0786: }
0787:
0788: /**
0789: * Sets the paint used to draw tick marks and sends an
0790: * {@link AxisChangeEvent} to all registered listeners.
0791: *
0792: * @param paint the paint (<code>null</code> not permitted).
0793: *
0794: * @see #getTickMarkPaint()
0795: */
0796: public void setTickMarkPaint(Paint paint) {
0797: if (paint == null) {
0798: throw new IllegalArgumentException("Null 'paint' argument.");
0799: }
0800: this .tickMarkPaint = paint;
0801: notifyListeners(new AxisChangeEvent(this ));
0802: }
0803:
0804: /**
0805: * Returns the plot that the axis is assigned to. This method will return
0806: * <code>null</code> if the axis is not currently assigned to a plot.
0807: *
0808: * @return The plot that the axis is assigned to (possibly
0809: * <code>null</code>).
0810: *
0811: * @see #setPlot(Plot)
0812: */
0813: public Plot getPlot() {
0814: return this .plot;
0815: }
0816:
0817: /**
0818: * Sets a reference to the plot that the axis is assigned to.
0819: * <P>
0820: * This method is used internally, you shouldn't need to call it yourself.
0821: *
0822: * @param plot the plot.
0823: *
0824: * @see #getPlot()
0825: */
0826: public void setPlot(Plot plot) {
0827: this .plot = plot;
0828: configure();
0829: }
0830:
0831: /**
0832: * Returns the fixed dimension for the axis.
0833: *
0834: * @return The fixed dimension.
0835: *
0836: * @see #setFixedDimension(double)
0837: */
0838: public double getFixedDimension() {
0839: return this .fixedDimension;
0840: }
0841:
0842: /**
0843: * Sets the fixed dimension for the axis.
0844: * <P>
0845: * This is used when combining more than one plot on a chart. In this case,
0846: * there may be several axes that need to have the same height or width so
0847: * that they are aligned. This method is used to fix a dimension for the
0848: * axis (the context determines whether the dimension is horizontal or
0849: * vertical).
0850: *
0851: * @param dimension the fixed dimension.
0852: *
0853: * @see #getFixedDimension()
0854: */
0855: public void setFixedDimension(double dimension) {
0856: this .fixedDimension = dimension;
0857: }
0858:
0859: /**
0860: * Configures the axis to work with the current plot. Override this method
0861: * to perform any special processing (such as auto-rescaling).
0862: */
0863: public abstract void configure();
0864:
0865: /**
0866: * Estimates the space (height or width) required to draw the axis.
0867: *
0868: * @param g2 the graphics device.
0869: * @param plot the plot that the axis belongs to.
0870: * @param plotArea the area within which the plot (including axes) should
0871: * be drawn.
0872: * @param edge the axis location.
0873: * @param space space already reserved.
0874: *
0875: * @return The space required to draw the axis (including pre-reserved
0876: * space).
0877: */
0878: public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot,
0879: Rectangle2D plotArea, RectangleEdge edge, AxisSpace space);
0880:
0881: /**
0882: * Draws the axis on a Java 2D graphics device (such as the screen or a
0883: * printer).
0884: *
0885: * @param g2 the graphics device (<code>null</code> not permitted).
0886: * @param cursor the cursor location (determines where to draw the axis).
0887: * @param plotArea the area within which the axes and plot should be drawn.
0888: * @param dataArea the area within which the data should be drawn.
0889: * @param edge the axis location (<code>null</code> not permitted).
0890: * @param plotState collects information about the plot
0891: * (<code>null</code> permitted).
0892: *
0893: * @return The axis state (never <code>null</code>).
0894: */
0895: public abstract AxisState draw(Graphics2D g2, double cursor,
0896: Rectangle2D plotArea, Rectangle2D dataArea,
0897: RectangleEdge edge, PlotRenderingInfo plotState);
0898:
0899: /**
0900: * Calculates the positions of the ticks for the axis, storing the results
0901: * in the tick list (ready for drawing).
0902: *
0903: * @param g2 the graphics device.
0904: * @param state the axis state.
0905: * @param dataArea the area inside the axes.
0906: * @param edge the edge on which the axis is located.
0907: *
0908: * @return The list of ticks.
0909: */
0910: public abstract List refreshTicks(Graphics2D g2, AxisState state,
0911: Rectangle2D dataArea, RectangleEdge edge);
0912:
0913: /**
0914: * Registers an object for notification of changes to the axis.
0915: *
0916: * @param listener the object that is being registered.
0917: *
0918: * @see #removeChangeListener(AxisChangeListener)
0919: */
0920: public void addChangeListener(AxisChangeListener listener) {
0921: this .listenerList.add(AxisChangeListener.class, listener);
0922: }
0923:
0924: /**
0925: * Deregisters an object for notification of changes to the axis.
0926: *
0927: * @param listener the object to deregister.
0928: *
0929: * @see #addChangeListener(AxisChangeListener)
0930: */
0931: public void removeChangeListener(AxisChangeListener listener) {
0932: this .listenerList.remove(AxisChangeListener.class, listener);
0933: }
0934:
0935: /**
0936: * Returns <code>true</code> if the specified object is registered with
0937: * the dataset as a listener. Most applications won't need to call this
0938: * method, it exists mainly for use by unit testing code.
0939: *
0940: * @param listener the listener.
0941: *
0942: * @return A boolean.
0943: */
0944: public boolean hasListener(EventListener listener) {
0945: List list = Arrays.asList(this .listenerList.getListenerList());
0946: return list.contains(listener);
0947: }
0948:
0949: /**
0950: * Notifies all registered listeners that the axis has changed.
0951: * The AxisChangeEvent provides information about the change.
0952: *
0953: * @param event information about the change to the axis.
0954: */
0955: protected void notifyListeners(AxisChangeEvent event) {
0956:
0957: Object[] listeners = this .listenerList.getListenerList();
0958: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0959: if (listeners[i] == AxisChangeListener.class) {
0960: ((AxisChangeListener) listeners[i + 1])
0961: .axisChanged(event);
0962: }
0963: }
0964:
0965: }
0966:
0967: /**
0968: * Returns a rectangle that encloses the axis label. This is typically
0969: * used for layout purposes (it gives the maximum dimensions of the label).
0970: *
0971: * @param g2 the graphics device.
0972: * @param edge the edge of the plot area along which the axis is measuring.
0973: *
0974: * @return The enclosing rectangle.
0975: */
0976: protected Rectangle2D getLabelEnclosure(Graphics2D g2,
0977: RectangleEdge edge) {
0978:
0979: Rectangle2D result = new Rectangle2D.Double();
0980: String axisLabel = getLabel();
0981: if (axisLabel != null && !axisLabel.equals("")) {
0982: FontMetrics fm = g2.getFontMetrics(getLabelFont());
0983: Rectangle2D bounds = TextUtilities.getTextBounds(axisLabel,
0984: g2, fm);
0985: RectangleInsets insets = getLabelInsets();
0986: bounds = insets.createOutsetRectangle(bounds);
0987: double angle = getLabelAngle();
0988: if (edge == RectangleEdge.LEFT
0989: || edge == RectangleEdge.RIGHT) {
0990: angle = angle - Math.PI / 2.0;
0991: }
0992: double x = bounds.getCenterX();
0993: double y = bounds.getCenterY();
0994: AffineTransform transformer = AffineTransform
0995: .getRotateInstance(angle, x, y);
0996: Shape labelBounds = transformer
0997: .createTransformedShape(bounds);
0998: result = labelBounds.getBounds2D();
0999: }
1000:
1001: return result;
1002:
1003: }
1004:
1005: /**
1006: * Draws the axis label.
1007: *
1008: * @param label the label text.
1009: * @param g2 the graphics device.
1010: * @param plotArea the plot area.
1011: * @param dataArea the area inside the axes.
1012: * @param edge the location of the axis.
1013: * @param state the axis state (<code>null</code> not permitted).
1014: *
1015: * @return Information about the axis.
1016: */
1017: protected AxisState drawLabel(String label, Graphics2D g2,
1018: Rectangle2D plotArea, Rectangle2D dataArea,
1019: RectangleEdge edge, AxisState state) {
1020:
1021: // it is unlikely that 'state' will be null, but check anyway...
1022: if (state == null) {
1023: throw new IllegalArgumentException("Null 'state' argument.");
1024: }
1025:
1026: if ((label == null) || (label.equals(""))) {
1027: return state;
1028: }
1029:
1030: Font font = getLabelFont();
1031: RectangleInsets insets = getLabelInsets();
1032: g2.setFont(font);
1033: g2.setPaint(getLabelPaint());
1034: FontMetrics fm = g2.getFontMetrics();
1035: Rectangle2D labelBounds = TextUtilities.getTextBounds(label,
1036: g2, fm);
1037:
1038: if (edge == RectangleEdge.TOP) {
1039:
1040: AffineTransform t = AffineTransform.getRotateInstance(
1041: getLabelAngle(), labelBounds.getCenterX(),
1042: labelBounds.getCenterY());
1043: Shape rotatedLabelBounds = t
1044: .createTransformedShape(labelBounds);
1045: labelBounds = rotatedLabelBounds.getBounds2D();
1046: double labelx = dataArea.getCenterX();
1047: double labely = state.getCursor() - insets.getBottom()
1048: - labelBounds.getHeight() / 2.0;
1049: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1050: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1051: TextAnchor.CENTER);
1052: state.cursorUp(insets.getTop() + labelBounds.getHeight()
1053: + insets.getBottom());
1054:
1055: } else if (edge == RectangleEdge.BOTTOM) {
1056:
1057: AffineTransform t = AffineTransform.getRotateInstance(
1058: getLabelAngle(), labelBounds.getCenterX(),
1059: labelBounds.getCenterY());
1060: Shape rotatedLabelBounds = t
1061: .createTransformedShape(labelBounds);
1062: labelBounds = rotatedLabelBounds.getBounds2D();
1063: double labelx = dataArea.getCenterX();
1064: double labely = state.getCursor() + insets.getTop()
1065: + labelBounds.getHeight() / 2.0;
1066: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1067: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1068: TextAnchor.CENTER);
1069: state.cursorDown(insets.getTop() + labelBounds.getHeight()
1070: + insets.getBottom());
1071:
1072: } else if (edge == RectangleEdge.LEFT) {
1073:
1074: AffineTransform t = AffineTransform.getRotateInstance(
1075: getLabelAngle() - Math.PI / 2.0, labelBounds
1076: .getCenterX(), labelBounds.getCenterY());
1077: Shape rotatedLabelBounds = t
1078: .createTransformedShape(labelBounds);
1079: labelBounds = rotatedLabelBounds.getBounds2D();
1080: double labelx = state.getCursor() - insets.getRight()
1081: - labelBounds.getWidth() / 2.0;
1082: double labely = dataArea.getCenterY();
1083: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1084: (float) labely, TextAnchor.CENTER, getLabelAngle()
1085: - Math.PI / 2.0, TextAnchor.CENTER);
1086: state.cursorLeft(insets.getLeft() + labelBounds.getWidth()
1087: + insets.getRight());
1088: } else if (edge == RectangleEdge.RIGHT) {
1089:
1090: AffineTransform t = AffineTransform.getRotateInstance(
1091: getLabelAngle() + Math.PI / 2.0, labelBounds
1092: .getCenterX(), labelBounds.getCenterY());
1093: Shape rotatedLabelBounds = t
1094: .createTransformedShape(labelBounds);
1095: labelBounds = rotatedLabelBounds.getBounds2D();
1096: double labelx = state.getCursor() + insets.getLeft()
1097: + labelBounds.getWidth() / 2.0;
1098: double labely = dataArea.getY() + dataArea.getHeight()
1099: / 2.0;
1100: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1101: (float) labely, TextAnchor.CENTER, getLabelAngle()
1102: + Math.PI / 2.0, TextAnchor.CENTER);
1103: state.cursorRight(insets.getLeft() + labelBounds.getWidth()
1104: + insets.getRight());
1105:
1106: }
1107:
1108: return state;
1109:
1110: }
1111:
1112: /**
1113: * Draws an axis line at the current cursor position and edge.
1114: *
1115: * @param g2 the graphics device.
1116: * @param cursor the cursor position.
1117: * @param dataArea the data area.
1118: * @param edge the edge.
1119: */
1120: protected void drawAxisLine(Graphics2D g2, double cursor,
1121: Rectangle2D dataArea, RectangleEdge edge) {
1122:
1123: Line2D axisLine = null;
1124: if (edge == RectangleEdge.TOP) {
1125: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1126: dataArea.getMaxX(), cursor);
1127: } else if (edge == RectangleEdge.BOTTOM) {
1128: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1129: dataArea.getMaxX(), cursor);
1130: } else if (edge == RectangleEdge.LEFT) {
1131: axisLine = new Line2D.Double(cursor, dataArea.getY(),
1132: cursor, dataArea.getMaxY());
1133: } else if (edge == RectangleEdge.RIGHT) {
1134: axisLine = new Line2D.Double(cursor, dataArea.getY(),
1135: cursor, dataArea.getMaxY());
1136: }
1137: g2.setPaint(this .axisLinePaint);
1138: g2.setStroke(this .axisLineStroke);
1139: g2.draw(axisLine);
1140:
1141: }
1142:
1143: /**
1144: * Returns a clone of the axis.
1145: *
1146: * @return A clone.
1147: *
1148: * @throws CloneNotSupportedException if some component of the axis does
1149: * not support cloning.
1150: */
1151: public Object clone() throws CloneNotSupportedException {
1152: Axis clone = (Axis) super .clone();
1153: // It's up to the plot which clones up to restore the correct references
1154: clone.plot = null;
1155: clone.listenerList = new EventListenerList();
1156: return clone;
1157: }
1158:
1159: /**
1160: * Tests this axis for equality with another object.
1161: *
1162: * @param obj the object (<code>null</code> permitted).
1163: *
1164: * @return <code>true</code> or <code>false</code>.
1165: */
1166: public boolean equals(Object obj) {
1167: if (obj == this ) {
1168: return true;
1169: }
1170: if (!(obj instanceof Axis)) {
1171: return false;
1172: }
1173: Axis that = (Axis) obj;
1174: if (this .visible != that.visible) {
1175: return false;
1176: }
1177: if (!ObjectUtilities.equal(this .label, that.label)) {
1178: return false;
1179: }
1180: if (!ObjectUtilities.equal(this .labelFont, that.labelFont)) {
1181: return false;
1182: }
1183: if (!PaintUtilities.equal(this .labelPaint, that.labelPaint)) {
1184: return false;
1185: }
1186: if (!ObjectUtilities.equal(this .labelInsets, that.labelInsets)) {
1187: return false;
1188: }
1189: if (this .labelAngle != that.labelAngle) {
1190: return false;
1191: }
1192: if (this .axisLineVisible != that.axisLineVisible) {
1193: return false;
1194: }
1195: if (!ObjectUtilities.equal(this .axisLineStroke,
1196: that.axisLineStroke)) {
1197: return false;
1198: }
1199: if (!PaintUtilities.equal(this .axisLinePaint,
1200: that.axisLinePaint)) {
1201: return false;
1202: }
1203: if (this .tickLabelsVisible != that.tickLabelsVisible) {
1204: return false;
1205: }
1206: if (!ObjectUtilities.equal(this .tickLabelFont,
1207: that.tickLabelFont)) {
1208: return false;
1209: }
1210: if (!PaintUtilities.equal(this .tickLabelPaint,
1211: that.tickLabelPaint)) {
1212: return false;
1213: }
1214: if (!ObjectUtilities.equal(this .tickLabelInsets,
1215: that.tickLabelInsets)) {
1216: return false;
1217: }
1218: if (this .tickMarksVisible != that.tickMarksVisible) {
1219: return false;
1220: }
1221: if (this .tickMarkInsideLength != that.tickMarkInsideLength) {
1222: return false;
1223: }
1224: if (this .tickMarkOutsideLength != that.tickMarkOutsideLength) {
1225: return false;
1226: }
1227: if (!PaintUtilities.equal(this .tickMarkPaint,
1228: that.tickMarkPaint)) {
1229: return false;
1230: }
1231: if (!ObjectUtilities.equal(this .tickMarkStroke,
1232: that.tickMarkStroke)) {
1233: return false;
1234: }
1235: if (this .fixedDimension != that.fixedDimension) {
1236: return false;
1237: }
1238: return true;
1239: }
1240:
1241: /**
1242: * Provides serialization support.
1243: *
1244: * @param stream the output stream.
1245: *
1246: * @throws IOException if there is an I/O error.
1247: */
1248: private void writeObject(ObjectOutputStream stream)
1249: throws IOException {
1250: stream.defaultWriteObject();
1251: SerialUtilities.writePaint(this .labelPaint, stream);
1252: SerialUtilities.writePaint(this .tickLabelPaint, stream);
1253: SerialUtilities.writeStroke(this .axisLineStroke, stream);
1254: SerialUtilities.writePaint(this .axisLinePaint, stream);
1255: SerialUtilities.writeStroke(this .tickMarkStroke, stream);
1256: SerialUtilities.writePaint(this .tickMarkPaint, stream);
1257: }
1258:
1259: /**
1260: * Provides serialization support.
1261: *
1262: * @param stream the input stream.
1263: *
1264: * @throws IOException if there is an I/O error.
1265: * @throws ClassNotFoundException if there is a classpath problem.
1266: */
1267: private void readObject(ObjectInputStream stream)
1268: throws IOException, ClassNotFoundException {
1269: stream.defaultReadObject();
1270: this .labelPaint = SerialUtilities.readPaint(stream);
1271: this .tickLabelPaint = SerialUtilities.readPaint(stream);
1272: this .axisLineStroke = SerialUtilities.readStroke(stream);
1273: this .axisLinePaint = SerialUtilities.readPaint(stream);
1274: this .tickMarkStroke = SerialUtilities.readStroke(stream);
1275: this .tickMarkPaint = SerialUtilities.readPaint(stream);
1276: this .listenerList = new EventListenerList();
1277: }
1278:
1279: }
|