    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jfreechart/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * ---------------------
    028 * PaintScaleLegend.java
    029 * ---------------------
    030 * (C) Copyright 2007-2009, by Object Refinery Limited.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   Peter Kolb - see patch 2686872;
    034 *
    035 * Changes
    036 * -------
    037 * 22-Jan-2007 : Version 1 (DG);
    038 * 18-Jun-2008 : Fixed bug drawing scale with log axis (DG);
    039 * 16-Apr-2009 : Patch 2686872 implementing AxisChangeListener, and fix for
    040 *               ignored stripOutlineVisible flag (DG);
    041 *
    042 */
    043
    044package org.jfree.chart.title;
    045
    046import java.awt.BasicStroke;
    047import java.awt.Color;
    048import java.awt.Graphics2D;
    049import java.awt.Paint;
    050import java.awt.Stroke;
    051import java.awt.geom.Rectangle2D;
    052import java.io.IOException;
    053import java.io.ObjectInputStream;
    054import java.io.ObjectOutputStream;
    055
    056import org.jfree.chart.axis.AxisLocation;
    057import org.jfree.chart.axis.AxisSpace;
    058import org.jfree.chart.axis.ValueAxis;
    059import org.jfree.chart.block.LengthConstraintType;
    060import org.jfree.chart.block.RectangleConstraint;
    061import org.jfree.chart.event.AxisChangeEvent;
    062import org.jfree.chart.event.AxisChangeListener;
    063import org.jfree.chart.event.TitleChangeEvent;
    064import org.jfree.chart.plot.Plot;
    065import org.jfree.chart.plot.PlotOrientation;
    066import org.jfree.chart.renderer.PaintScale;
    067import org.jfree.data.Range;
    068import org.jfree.io.SerialUtilities;
    069import org.jfree.ui.RectangleEdge;
    070import org.jfree.ui.Size2D;
    071import org.jfree.util.PaintUtilities;
    072import org.jfree.util.PublicCloneable;
    073
    074/**
    075 * A legend that shows a range of values and their associated colors, driven
    076 * by an underlying {@link PaintScale} implementation.
    077 *
    078 * @since 1.0.4
    079 */
    080public class PaintScaleLegend extends Title implements AxisChangeListener,
    081        PublicCloneable {
    082
    083    /** For serialization. */
    084    static final long serialVersionUID = -1365146490993227503L;
    085
    086    /** The paint scale (never <code>null</code>). */
    087    private PaintScale scale;
    088
    089    /** The value axis (never <code>null</code>). */
    090    private ValueAxis axis;
    091
    092    /**
    093     * The axis location (handles both orientations, never
    094     * <code>null</code>).
    095     */
    096    private AxisLocation axisLocation;
    097
    098    /** The offset between the axis and the paint strip (in Java2D units). */
    099    private double axisOffset;
    100
    101    /** The thickness of the paint strip (in Java2D units). */
    102    private double stripWidth;
    103
    104    /**
    105     * A flag that controls whether or not an outline is drawn around the
    106     * paint strip.
    107     */
    108    private boolean stripOutlineVisible;
    109
    110    /** The paint used to draw an outline around the paint strip. */
    111    private transient Paint stripOutlinePaint;
    112
    113    /** The stroke used to draw an outline around the paint strip. */
    114    private transient Stroke stripOutlineStroke;
    115
    116    /** The background paint (never <code>null</code>). */
    117    private transient Paint backgroundPaint;
    118
    119    /**
    120     * The number of subdivisions for the scale when rendering.
    121     *
    122     * @since 1.0.11
    123     */
    124    private int subdivisions;
    125
    126    /**
    127     * Creates a new instance.
    128     *
    129     * @param scale  the scale (<code>null</code> not permitted).
    130     * @param axis  the axis (<code>null</code> not permitted).
    131     */
    132    public PaintScaleLegend(PaintScale scale, ValueAxis axis) {
    133        if (axis == null) {
    134            throw new IllegalArgumentException("Null 'axis' argument.");
    135        }
    136        this.scale = scale;
    137        this.axis = axis;
    138        this.axis.addChangeListener(this);
    139        this.axisLocation = AxisLocation.BOTTOM_OR_LEFT;
    140        this.axisOffset = 0.0;
    141        this.axis.setRange(scale.getLowerBound(), scale.getUpperBound());
    142        this.stripWidth = 15.0;
    143        this.stripOutlineVisible = true;
    144        this.stripOutlinePaint = Color.gray;
    145        this.stripOutlineStroke = new BasicStroke(0.5f);
    146        this.backgroundPaint = Color.white;
    147        this.subdivisions = 100;
    148    }
    149
    150    /**
    151     * Returns the scale used to convert values to colors.
    152     *
    153     * @return The scale (never <code>null</code>).
    154     *
    155     * @see #setScale(PaintScale)
    156     */
    157    public PaintScale getScale() {
    158        return this.scale;
    159    }
    160
    161    /**
    162     * Sets the scale and sends a {@link TitleChangeEvent} to all registered
    163     * listeners.
    164     *
    165     * @param scale  the scale (<code>null</code> not permitted).
    166     *
    167     * @see #getScale()
    168     */
    169    public void setScale(PaintScale scale) {
    170        if (scale == null) {
    171            throw new IllegalArgumentException("Null 'scale' argument.");
    172        }
    173        this.scale = scale;
    174        notifyListeners(new TitleChangeEvent(this));
    175    }
    176
    177    /**
    178     * Returns the axis for the paint scale.
    179     *
    180     * @return The axis (never <code>null</code>).
    181     *
    182     * @see #setAxis(ValueAxis)
    183     */
    184    public ValueAxis getAxis() {
    185        return this.axis;
    186    }
    187
    188    /**
    189     * Sets the axis for the paint scale and sends a {@link TitleChangeEvent}
    190     * to all registered listeners.
    191     *
    192     * @param axis  the axis (<code>null</code> not permitted).
    193     *
    194     * @see #getAxis()
    195     */
    196    public void setAxis(ValueAxis axis) {
    197        if (axis == null) {
    198            throw new IllegalArgumentException("Null 'axis' argument.");
    199        }
    200        this.axis.removeChangeListener(this);
    201        this.axis = axis;
    202        this.axis.addChangeListener(this);
    203        notifyListeners(new TitleChangeEvent(this));
    204    }
    205
    206    /**
    207     * Returns the axis location.
    208     *
    209     * @return The axis location (never <code>null</code>).
    210     *
    211     * @see #setAxisLocation(AxisLocation)
    212     */
    213    public AxisLocation getAxisLocation() {
    214        return this.axisLocation;
    215    }
    216
    217    /**
    218     * Sets the axis location and sends a {@link TitleChangeEvent} to all
    219     * registered listeners.
    220     *
    221     * @param location  the location (<code>null</code> not permitted).
    222     *
    223     * @see #getAxisLocation()
    224     */
    225    public void setAxisLocation(AxisLocation location) {
    226        if (location == null) {
    227            throw new IllegalArgumentException("Null 'location' argument.");
    228        }
    229        this.axisLocation = location;
    230        notifyListeners(new TitleChangeEvent(this));
    231    }
    232
    233    /**
    234     * Returns the offset between the axis and the paint strip.
    235     *
    236     * @return The offset between the axis and the paint strip.
    237     *
    238     * @see #setAxisOffset(double)
    239     */
    240    public double getAxisOffset() {
    241        return this.axisOffset;
    242    }
    243
    244    /**
    245     * Sets the offset between the axis and the paint strip and sends a
    246     * {@link TitleChangeEvent} to all registered listeners.
    247     *
    248     * @param offset  the offset.
    249     */
    250    public void setAxisOffset(double offset) {
    251        this.axisOffset = offset;
    252        notifyListeners(new TitleChangeEvent(this));
    253    }
    254
    255    /**
    256     * Returns the width of the paint strip, in Java2D units.
    257     *
    258     * @return The width of the paint strip.
    259     *
    260     * @see #setStripWidth(double)
    261     */
    262    public double getStripWidth() {
    263        return this.stripWidth;
    264    }
    265
    266    /**
    267     * Sets the width of the paint strip and sends a {@link TitleChangeEvent}
    268     * to all registered listeners.
    269     *
    270     * @param width  the width.
    271     *
    272     * @see #getStripWidth()
    273     */
    274    public void setStripWidth(double width) {
    275        this.stripWidth = width;
    276        notifyListeners(new TitleChangeEvent(this));
    277    }
    278
    279    /**
    280     * Returns the flag that controls whether or not an outline is drawn
    281     * around the paint strip.
    282     *
    283     * @return A boolean.
    284     *
    285     * @see #setStripOutlineVisible(boolean)
    286     */
    287    public boolean isStripOutlineVisible() {
    288        return this.stripOutlineVisible;
    289    }
    290
    291    /**
    292     * Sets the flag that controls whether or not an outline is drawn around
    293     * the paint strip, and sends a {@link TitleChangeEvent} to all registered
    294     * listeners.
    295     *
    296     * @param visible  the flag.
    297     *
    298     * @see #isStripOutlineVisible()
    299     */
    300    public void setStripOutlineVisible(boolean visible) {
    301        this.stripOutlineVisible = visible;
    302        notifyListeners(new TitleChangeEvent(this));
    303    }
    304
    305    /**
    306     * Returns the paint used to draw the outline of the paint strip.
    307     *
    308     * @return The paint (never <code>null</code>).
    309     *
    310     * @see #setStripOutlinePaint(Paint)
    311     */
    312    public Paint getStripOutlinePaint() {
    313        return this.stripOutlinePaint;
    314    }
    315
    316    /**
    317     * Sets the paint used to draw the outline of the paint strip, and sends
    318     * a {@link TitleChangeEvent} to all registered listeners.
    319     *
    320     * @param paint  the paint (<code>null</code> not permitted).
    321     *
    322     * @see #getStripOutlinePaint()
    323     */
    324    public void setStripOutlinePaint(Paint paint) {
    325        if (paint == null) {
    326            throw new IllegalArgumentException("Null 'paint' argument.");
    327        }
    328        this.stripOutlinePaint = paint;
    329        notifyListeners(new TitleChangeEvent(this));
    330    }
    331
    332    /**
    333     * Returns the stroke used to draw the outline around the paint strip.
    334     *
    335     * @return The stroke (never <code>null</code>).
    336     *
    337     * @see #setStripOutlineStroke(Stroke)
    338     */
    339    public Stroke getStripOutlineStroke() {
    340        return this.stripOutlineStroke;
    341    }
    342
    343    /**
    344     * Sets the stroke used to draw the outline around the paint strip and
    345     * sends a {@link TitleChangeEvent} to all registered listeners.
    346     *
    347     * @param stroke  the stroke (<code>null</code> not permitted).
    348     *
    349     * @see #getStripOutlineStroke()
    350     */
    351    public void setStripOutlineStroke(Stroke stroke) {
    352        if (stroke == null) {
    353            throw new IllegalArgumentException("Null 'stroke' argument.");
    354        }
    355        this.stripOutlineStroke = stroke;
    356        notifyListeners(new TitleChangeEvent(this));
    357    }
    358
    359    /**
    360     * Returns the background paint.
    361     *
    362     * @return The background paint.
    363     */
    364    public Paint getBackgroundPaint() {
    365        return this.backgroundPaint;
    366    }
    367
    368    /**
    369     * Sets the background paint and sends a {@link TitleChangeEvent} to all
    370     * registered listeners.
    371     *
    372     * @param paint  the paint (<code>null</code> permitted).
    373     */
    374    public void setBackgroundPaint(Paint paint) {
    375        this.backgroundPaint = paint;
    376        notifyListeners(new TitleChangeEvent(this));
    377    }
    378
    379    /**
    380     * Returns the number of subdivisions used to draw the scale.
    381     *
    382     * @return The subdivision count.
    383     *
    384     * @since 1.0.11
    385     */
    386    public int getSubdivisionCount() {
    387        return this.subdivisions;
    388    }
    389
    390    /**
    391     * Sets the subdivision count and sends a {@link TitleChangeEvent} to
    392     * all registered listeners.
    393     *
    394     * @param count  the count.
    395     *
    396     * @since 1.0.11
    397     */
    398    public void setSubdivisionCount(int count) {
    399        if (count <= 0) {
    400            throw new IllegalArgumentException("Requires 'count' > 0.");
    401        }
    402        this.subdivisions = count;
    403        notifyListeners(new TitleChangeEvent(this));
    404    }
    405
    406    /**
    407     * Receives notification of an axis change event and responds by firing
    408     * a title change event.
    409     *
    410     * @param event  the event.
    411     *
    412     * @since 1.0.13
    413     */
    414    public void axisChanged(AxisChangeEvent event) {
    415        if (this.axis == event.getAxis()) {
    416            notifyListeners(new TitleChangeEvent(this));
    417        }
    418    }
    419
    420    /**
    421     * Arranges the contents of the block, within the given constraints, and
    422     * returns the block size.
    423     *
    424     * @param g2  the graphics device.
    425     * @param constraint  the constraint (<code>null</code> not permitted).
    426     *
    427     * @return The block size (in Java2D units, never <code>null</code>).
    428     */
    429    public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
    430        RectangleConstraint cc = toContentConstraint(constraint);
    431        LengthConstraintType w = cc.getWidthConstraintType();
    432        LengthConstraintType h = cc.getHeightConstraintType();
    433        Size2D contentSize = null;
    434        if (w == LengthConstraintType.NONE) {
    435            if (h == LengthConstraintType.NONE) {
    436                contentSize = new Size2D(getWidth(), getHeight());
    437            }
    438            else if (h == LengthConstraintType.RANGE) {
    439                throw new RuntimeException("Not yet implemented.");
    440            }
    441            else if (h == LengthConstraintType.FIXED) {
    442                throw new RuntimeException("Not yet implemented.");
    443            }
    444        }
    445        else if (w == LengthConstraintType.RANGE) {
    446            if (h == LengthConstraintType.NONE) {
    447                throw new RuntimeException("Not yet implemented.");
    448            }
    449            else if (h == LengthConstraintType.RANGE) {
    450                contentSize = arrangeRR(g2, cc.getWidthRange(),
    451                        cc.getHeightRange());
    452            }
    453            else if (h == LengthConstraintType.FIXED) {
    454                throw new RuntimeException("Not yet implemented.");
    455            }
    456        }
    457        else if (w == LengthConstraintType.FIXED) {
    458            if (h == LengthConstraintType.NONE) {
    459                throw new RuntimeException("Not yet implemented.");
    460            }
    461            else if (h == LengthConstraintType.RANGE) {
    462                throw new RuntimeException("Not yet implemented.");
    463            }
    464            else if (h == LengthConstraintType.FIXED) {
    465                throw new RuntimeException("Not yet implemented.");
    466            }
    467        }
    468        return new Size2D(calculateTotalWidth(contentSize.getWidth()),
    469                calculateTotalHeight(contentSize.getHeight()));
    470    }
    471
    472    /**
    473     * Returns the content size for the title.  This will reflect the fact that
    474     * a text title positioned on the left or right of a chart will be rotated
    475     * 90 degrees.
    476     *
    477     * @param g2  the graphics device.
    478     * @param widthRange  the width range.
    479     * @param heightRange  the height range.
    480     *
    481     * @return The content size.
    482     */
    483    protected Size2D arrangeRR(Graphics2D g2, Range widthRange,
    484            Range heightRange) {
    485
    486        RectangleEdge position = getPosition();
    487        if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) {
    488
    489
    490            float maxWidth = (float) widthRange.getUpperBound();
    491
    492            // determine the space required for the axis
    493            AxisSpace space = this.axis.reserveSpace(g2, null,
    494                    new Rectangle2D.Double(0, 0, maxWidth, 100),
    495                    RectangleEdge.BOTTOM, null);
    496
    497            return new Size2D(maxWidth, this.stripWidth + this.axisOffset
    498                    + space.getTop() + space.getBottom());
    499        }
    500        else if (position == RectangleEdge.LEFT || position
    501                == RectangleEdge.RIGHT) {
    502            float maxHeight = (float) heightRange.getUpperBound();
    503            AxisSpace space = this.axis.reserveSpace(g2, null,
    504                    new Rectangle2D.Double(0, 0, 100, maxHeight),
    505                    RectangleEdge.RIGHT, null);
    506            return new Size2D(this.stripWidth + this.axisOffset
    507                    + space.getLeft() + space.getRight(), maxHeight);
    508        }
    509        else {
    510            throw new RuntimeException("Unrecognised position.");
    511        }
    512    }
    513
    514    /**
    515     * Draws the legend within the specified area.
    516     *
    517     * @param g2  the graphics target (<code>null</code> not permitted).
    518     * @param area  the drawing area (<code>null</code> not permitted).
    519     */
    520    public void draw(Graphics2D g2, Rectangle2D area) {
    521        draw(g2, area, null);
    522    }
    523
    524    /**
    525     * Draws the legend within the specified area.
    526     *
    527     * @param g2  the graphics target (<code>null</code> not permitted).
    528     * @param area  the drawing area (<code>null</code> not permitted).
    529     * @param params  drawing parameters (ignored here).
    530     *
    531     * @return <code>null</code>.
    532     */
    533    public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
    534
    535        Rectangle2D target = (Rectangle2D) area.clone();
    536        target = trimMargin(target);
    537        if (this.backgroundPaint != null) {
    538            g2.setPaint(this.backgroundPaint);
    539            g2.fill(target);
    540        }
    541        getFrame().draw(g2, target);
    542        getFrame().getInsets().trim(target);
    543        target = trimPadding(target);
    544        double base = this.axis.getLowerBound();
    545        double increment = this.axis.getRange().getLength() / this.subdivisions;
    546        Rectangle2D r = new Rectangle2D.Double();
    547
    548        if (RectangleEdge.isTopOrBottom(getPosition())) {
    549            RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
    550                    this.axisLocation, PlotOrientation.HORIZONTAL);
    551            if (axisEdge == RectangleEdge.TOP) {
    552                for (int i = 0; i < this.subdivisions; i++) {
    553                    double v = base + (i * increment);
    554                    Paint p = this.scale.getPaint(v);
    555                    double vv0 = this.axis.valueToJava2D(v, target,
    556                            RectangleEdge.TOP);
    557                    double vv1 = this.axis.valueToJava2D(v + increment, target,
    558                            RectangleEdge.TOP);
    559                    double ww = Math.abs(vv1 - vv0) + 1.0;
    560                    r.setRect(Math.min(vv0, vv1), target.getMaxY()
    561                            - this.stripWidth, ww, this.stripWidth);
    562                    g2.setPaint(p);
    563                    g2.fill(r);
    564                }
    565                if (isStripOutlineVisible()) {
    566                    g2.setPaint(this.stripOutlinePaint);
    567                    g2.setStroke(this.stripOutlineStroke);
    568                    g2.draw(new Rectangle2D.Double(target.getMinX(),
    569                            target.getMaxY() - this.stripWidth,
    570                            target.getWidth(), this.stripWidth));
    571                }
    572                this.axis.draw(g2, target.getMaxY() - this.stripWidth
    573                        - this.axisOffset, target, target, RectangleEdge.TOP,
    574                        null);
    575            }
    576            else if (axisEdge == RectangleEdge.BOTTOM) {
    577                for (int i = 0; i < this.subdivisions; i++) {
    578                    double v = base + (i * increment);
    579                    Paint p = this.scale.getPaint(v);
    580                    double vv0 = this.axis.valueToJava2D(v, target,
    581                            RectangleEdge.BOTTOM);
    582                    double vv1 = this.axis.valueToJava2D(v + increment, target,
    583                            RectangleEdge.BOTTOM);
    584                    double ww = Math.abs(vv1 - vv0) + 1.0;
    585                    r.setRect(Math.min(vv0, vv1), target.getMinY(), ww,
    586                            this.stripWidth);
    587                    g2.setPaint(p);
    588                    g2.fill(r);
    589                }
    590                if (isStripOutlineVisible()) {
    591                    g2.setPaint(this.stripOutlinePaint);
    592                    g2.setStroke(this.stripOutlineStroke);
    593                    g2.draw(new Rectangle2D.Double(target.getMinX(),
    594                            target.getMinY(), target.getWidth(),
    595                            this.stripWidth));
    596                }
    597                this.axis.draw(g2, target.getMinY() + this.stripWidth
    598                        + this.axisOffset, target, target,
    599                        RectangleEdge.BOTTOM, null);
    600            }
    601        }
    602        else {
    603            RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
    604                    this.axisLocation, PlotOrientation.VERTICAL);
    605            if (axisEdge == RectangleEdge.LEFT) {
    606                for (int i = 0; i < this.subdivisions; i++) {
    607                    double v = base + (i * increment);
    608                    Paint p = this.scale.getPaint(v);
    609                    double vv0 = this.axis.valueToJava2D(v, target,
    610                            RectangleEdge.LEFT);
    611                    double vv1 = this.axis.valueToJava2D(v + increment, target,
    612                            RectangleEdge.LEFT);
    613                    double hh = Math.abs(vv1 - vv0) + 1.0;
    614                    r.setRect(target.getMaxX() - this.stripWidth,
    615                            Math.min(vv0, vv1), this.stripWidth, hh);
    616                    g2.setPaint(p);
    617                    g2.fill(r);
    618                }
    619                if (isStripOutlineVisible()) {
    620                    g2.setPaint(this.stripOutlinePaint);
    621                    g2.setStroke(this.stripOutlineStroke);
    622                    g2.draw(new Rectangle2D.Double(target.getMaxX()
    623                            - this.stripWidth, target.getMinY(),
    624                            this.stripWidth, target.getHeight()));
    625                }
    626                this.axis.draw(g2, target.getMaxX() - this.stripWidth
    627                        - this.axisOffset, target, target, RectangleEdge.LEFT,
    628                        null);
    629            }
    630            else if (axisEdge == RectangleEdge.RIGHT) {
    631                for (int i = 0; i < this.subdivisions; i++) {
    632                    double v = base + (i * increment);
    633                    Paint p = this.scale.getPaint(v);
    634                    double vv0 = this.axis.valueToJava2D(v, target,
    635                            RectangleEdge.LEFT);
    636                    double vv1 = this.axis.valueToJava2D(v + increment, target,
    637                            RectangleEdge.LEFT);
    638                    double hh = Math.abs(vv1 - vv0) + 1.0;
    639                    r.setRect(target.getMinX(), Math.min(vv0, vv1),
    640                            this.stripWidth, hh);
    641                    g2.setPaint(p);
    642                    g2.fill(r);
    643                }
    644                if (isStripOutlineVisible()) {
    645                    g2.setPaint(this.stripOutlinePaint);
    646                    g2.setStroke(this.stripOutlineStroke);
    647                    g2.draw(new Rectangle2D.Double(target.getMinX(),
    648                            target.getMinY(), this.stripWidth,
    649                            target.getHeight()));
    650                }
    651                this.axis.draw(g2, target.getMinX() + this.stripWidth
    652                        + this.axisOffset, target, target, RectangleEdge.RIGHT,
    653                        null);
    654            }
    655        }
    656        return null;
    657    }
    658
    659    /**
    660     * Tests this legend for equality with an arbitrary object.
    661     *
    662     * @param obj  the object (<code>null</code> permitted).
    663     *
    664     * @return A boolean.
    665     */
    666    public boolean equals(Object obj) {
    667        if (!(obj instanceof PaintScaleLegend)) {
    668            return false;
    669        }
    670        PaintScaleLegend that = (PaintScaleLegend) obj;
    671        if (!this.scale.equals(that.scale)) {
    672            return false;
    673        }
    674        if (!this.axis.equals(that.axis)) {
    675            return false;
    676        }
    677        if (!this.axisLocation.equals(that.axisLocation)) {
    678            return false;
    679        }
    680        if (this.axisOffset != that.axisOffset) {
    681            return false;
    682        }
    683        if (this.stripWidth != that.stripWidth) {
    684            return false;
    685        }
    686        if (this.stripOutlineVisible != that.stripOutlineVisible) {
    687            return false;
    688        }
    689        if (!PaintUtilities.equal(this.stripOutlinePaint,
    690                that.stripOutlinePaint)) {
    691            return false;
    692        }
    693        if (!this.stripOutlineStroke.equals(that.stripOutlineStroke)) {
    694            return false;
    695        }
    696        if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) {
    697            return false;
    698        }
    699        if (this.subdivisions != that.subdivisions) {
    700            return false;
    701        }
    702        return super.equals(obj);
    703    }
    704
    705    /**
    706     * Provides serialization support.
    707     *
    708     * @param stream  the output stream.
    709     *
    710     * @throws IOException  if there is an I/O error.
    711     */
    712    private void writeObject(ObjectOutputStream stream) throws IOException {
    713        stream.defaultWriteObject();
    714        SerialUtilities.writePaint(this.backgroundPaint, stream);
    715        SerialUtilities.writePaint(this.stripOutlinePaint, stream);
    716        SerialUtilities.writeStroke(this.stripOutlineStroke, stream);
    717    }
    718
    719    /**
    720     * Provides serialization support.
    721     *
    722     * @param stream  the input stream.
    723     *
    724     * @throws IOException  if there is an I/O error.
    725     * @throws ClassNotFoundException  if there is a classpath problem.
    726     */
    727    private void readObject(ObjectInputStream stream)
    728            throws IOException, ClassNotFoundException {
    729        stream.defaultReadObject();
    730        this.backgroundPaint = SerialUtilities.readPaint(stream);
    731        this.stripOutlinePaint = SerialUtilities.readPaint(stream);
    732        this.stripOutlineStroke = SerialUtilities.readStroke(stream);
    733    }
    734
    735}