    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * -------------------
    028     * LineRenderer3D.java
    029     * -------------------
    030     * (C) Copyright 2004-2008, by Tobias Selb and Contributors.
    031     *
    032     * Original Author:  Tobias Selb (http://www.uepselon.com);
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * Changes
    036     * -------
    037     * 15-Oct-2004 : Version 1 (TS);
    038     * 05-Nov-2004 : Modified drawItem() signature (DG);
    039     * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
    040     * 26-Jan-2005 : Update for changes in super class (DG);
    041     * 13-Apr-2005 : Check item visibility in drawItem() method (DG);
    042     * 09-Jun-2005 : Use addItemEntity() in drawItem() method (DG);
    043     * 10-Jun-2005 : Fixed capitalisation of setXOffset() and setYOffset() (DG);
    044     * ------------- JFREECHART 1.0.x ---------------------------------------------
    045     * 01-Dec-2006 : Fixed equals() and serialization (DG);
    046     * 17-Jan-2007 : Fixed bug in drawDomainGridline() method and added
    047     *               argument check to setWallPaint() (DG);
    048     * 03-Apr-2007 : Fixed bugs in drawBackground() method (DG);
    049     * 16-Oct-2007 : Fixed bug in range marker drawing (DG);
    050     *
    051     */
    052
    053    package org.jfree.chart.renderer.category;
    054
    055    import java.awt.AlphaComposite;
    056    import java.awt.Color;
    057    import java.awt.Composite;
    058    import java.awt.Graphics2D;
    059    import java.awt.Image;
    060    import java.awt.Paint;
    061    import java.awt.Shape;
    062    import java.awt.Stroke;
    063    import java.awt.geom.GeneralPath;
    064    import java.awt.geom.Line2D;
    065    import java.awt.geom.Rectangle2D;
    066    import java.io.IOException;
    067    import java.io.ObjectInputStream;
    068    import java.io.ObjectOutputStream;
    069    import java.io.Serializable;
    070
    071    import org.jfree.chart.Effect3D;
    072    import org.jfree.chart.axis.CategoryAxis;
    073    import org.jfree.chart.axis.ValueAxis;
    074    import org.jfree.chart.entity.EntityCollection;
    075    import org.jfree.chart.event.RendererChangeEvent;
    076    import org.jfree.chart.plot.CategoryPlot;
    077    import org.jfree.chart.plot.Marker;
    078    import org.jfree.chart.plot.PlotOrientation;
    079    import org.jfree.chart.plot.ValueMarker;
    080    import org.jfree.data.Range;
    081    import org.jfree.data.category.CategoryDataset;
    082    import org.jfree.io.SerialUtilities;
    083    import org.jfree.util.PaintUtilities;
    084    import org.jfree.util.ShapeUtilities;
    085
    086    /**
    087     * A line renderer with a 3D effect.  The example shown here is generated by
    088     * the <code>LineChart3DDemo1.java</code> program included in the JFreeChart
    089     * Demo Collection:
    090     * <br><br>
    091     * <img src="../../../../../images/LineRenderer3DSample.png"
    092     * alt="LineRenderer3DSample.png" />
    093     */
    094    public class LineRenderer3D extends LineAndShapeRenderer
    095                                implements Effect3D, Serializable {
    096
    097        /** For serialization. */
    098        private static final long serialVersionUID = 5467931468380928736L;
    099
    100        /** The default x-offset for the 3D effect. */
    101        public static final double DEFAULT_X_OFFSET = 12.0;
    102
    103        /** The default y-offset for the 3D effect. */
    104        public static final double DEFAULT_Y_OFFSET = 8.0;
    105
    106        /** The default wall paint. */
    107        public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD);
    108
    109        /** The size of x-offset for the 3D effect. */
    110        private double xOffset;
    111
    112        /** The size of y-offset for the 3D effect. */
    113        private double yOffset;
    114
    115        /** The paint used to shade the left and lower 3D wall. */
    116        private transient Paint wallPaint;
    117
    118        /**
    119         * Creates a new renderer.
    120         */
    121        public LineRenderer3D() {
    122            super(true, false);  //Create a line renderer only
    123            this.xOffset = DEFAULT_X_OFFSET;
    124            this.yOffset = DEFAULT_Y_OFFSET;
    125            this.wallPaint = DEFAULT_WALL_PAINT;
    126        }
    127
    128        /**
    129         * Returns the x-offset for the 3D effect.
    130         *
    131         * @return The x-offset.
    132         *
    133         * @see #setXOffset(double)
    134         * @see #getYOffset()
    135         */
    136        public double getXOffset() {
    137            return this.xOffset;
    138        }
    139
    140        /**
    141         * Returns the y-offset for the 3D effect.
    142         *
    143         * @return The y-offset.
    144         *
    145         * @see #setYOffset(double)
    146         * @see #getXOffset()
    147         */
    148        public double getYOffset() {
    149            return this.yOffset;
    150        }
    151
    152        /**
    153         * Sets the x-offset and sends a {@link RendererChangeEvent} to all
    154         * registered listeners.
    155         *
    156         * @param xOffset  the x-offset.
    157         *
    158         * @see #getXOffset()
    159         */
    160        public void setXOffset(double xOffset) {
    161            this.xOffset = xOffset;
    162            fireChangeEvent();
    163        }
    164
    165        /**
    166         * Sets the y-offset and sends a {@link RendererChangeEvent} to all
    167         * registered listeners.
    168         *
    169         * @param yOffset  the y-offset.
    170         *
    171         * @see #getYOffset()
    172         */
    173        public void setYOffset(double yOffset) {
    174            this.yOffset = yOffset;
    175            fireChangeEvent();
    176        }
    177
    178        /**
    179         * Returns the paint used to highlight the left and bottom wall in the plot
    180         * background.
    181         *
    182         * @return The paint.
    183         *
    184         * @see #setWallPaint(Paint)
    185         */
    186        public Paint getWallPaint() {
    187            return this.wallPaint;
    188        }
    189
    190        /**
    191         * Sets the paint used to hightlight the left and bottom walls in the plot
    192         * background, and sends a {@link RendererChangeEvent} to all
    193         * registered listeners.
    194         *
    195         * @param paint  the paint (<code>null</code> not permitted).
    196         *
    197         * @see #getWallPaint()
    198         */
    199        public void setWallPaint(Paint paint) {
    200            if (paint == null) {
    201                throw new IllegalArgumentException("Null 'paint' argument.");
    202            }
    203            this.wallPaint = paint;
    204            fireChangeEvent();
    205        }
    206
    207        /**
    208         * Draws the background for the plot.
    209         *
    210         * @param g2  the graphics device.
    211         * @param plot  the plot.
    212         * @param dataArea  the area inside the axes.
    213         */
    214        public void drawBackground(Graphics2D g2, CategoryPlot plot,
    215                                   Rectangle2D dataArea) {
    216
    217            float x0 = (float) dataArea.getX();
    218            float x1 = x0 + (float) Math.abs(this.xOffset);
    219            float x3 = (float) dataArea.getMaxX();
    220            float x2 = x3 - (float) Math.abs(this.xOffset);
    221
    222            float y0 = (float) dataArea.getMaxY();
    223            float y1 = y0 - (float) Math.abs(this.yOffset);
    224            float y3 = (float) dataArea.getMinY();
    225            float y2 = y3 + (float) Math.abs(this.yOffset);
    226
    227            GeneralPath clip = new GeneralPath();
    228            clip.moveTo(x0, y0);
    229            clip.lineTo(x0, y2);
    230            clip.lineTo(x1, y3);
    231            clip.lineTo(x3, y3);
    232            clip.lineTo(x3, y1);
    233            clip.lineTo(x2, y0);
    234            clip.closePath();
    235
    236            Composite originalComposite = g2.getComposite();
    237            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
    238                    plot.getBackgroundAlpha()));
    239
    240            // fill background...
    241            Paint backgroundPaint = plot.getBackgroundPaint();
    242            if (backgroundPaint != null) {
    243                g2.setPaint(backgroundPaint);
    244                g2.fill(clip);
    245            }
    246
    247            GeneralPath leftWall = new GeneralPath();
    248            leftWall.moveTo(x0, y0);
    249            leftWall.lineTo(x0, y2);
    250            leftWall.lineTo(x1, y3);
    251            leftWall.lineTo(x1, y1);
    252            leftWall.closePath();
    253            g2.setPaint(getWallPaint());
    254            g2.fill(leftWall);
    255
    256            GeneralPath bottomWall = new GeneralPath();
    257            bottomWall.moveTo(x0, y0);
    258            bottomWall.lineTo(x1, y1);
    259            bottomWall.lineTo(x3, y1);
    260            bottomWall.lineTo(x2, y0);
    261            bottomWall.closePath();
    262            g2.setPaint(getWallPaint());
    263            g2.fill(bottomWall);
    264
    265            // higlight the background corners...
    266            g2.setPaint(Color.lightGray);
    267            Line2D corner = new Line2D.Double(x0, y0, x1, y1);
    268            g2.draw(corner);
    269            corner.setLine(x1, y1, x1, y3);
    270            g2.draw(corner);
    271            corner.setLine(x1, y1, x3, y1);
    272            g2.draw(corner);
    273
    274            // draw background image, if there is one...
    275            Image backgroundImage = plot.getBackgroundImage();
    276            if (backgroundImage != null) {
    277                Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX()
    278                        + getXOffset(), dataArea.getY(),
    279                        dataArea.getWidth() - getXOffset(),
    280                        dataArea.getHeight() - getYOffset());
    281                plot.drawBackgroundImage(g2, adjusted);
    282            }
    283
    284            g2.setComposite(originalComposite);
    285
    286        }
    287
    288        /**
    289         * Draws the outline for the plot.
    290         *
    291         * @param g2  the graphics device.
    292         * @param plot  the plot.
    293         * @param dataArea  the area inside the axes.
    294         */
    295        public void drawOutline(Graphics2D g2, CategoryPlot plot,
    296                                Rectangle2D dataArea) {
    297
    298            float x0 = (float) dataArea.getX();
    299            float x1 = x0 + (float) Math.abs(this.xOffset);
    300            float x3 = (float) dataArea.getMaxX();
    301            float x2 = x3 - (float) Math.abs(this.xOffset);
    302
    303            float y0 = (float) dataArea.getMaxY();
    304            float y1 = y0 - (float) Math.abs(this.yOffset);
    305            float y3 = (float) dataArea.getMinY();
    306            float y2 = y3 + (float) Math.abs(this.yOffset);
    307
    308            GeneralPath clip = new GeneralPath();
    309            clip.moveTo(x0, y0);
    310            clip.lineTo(x0, y2);
    311            clip.lineTo(x1, y3);
    312            clip.lineTo(x3, y3);
    313            clip.lineTo(x3, y1);
    314            clip.lineTo(x2, y0);
    315            clip.closePath();
    316
    317            // put an outline around the data area...
    318            Stroke outlineStroke = plot.getOutlineStroke();
    319            Paint outlinePaint = plot.getOutlinePaint();
    320            if ((outlineStroke != null) && (outlinePaint != null)) {
    321                g2.setStroke(outlineStroke);
    322                g2.setPaint(outlinePaint);
    323                g2.draw(clip);
    324            }
    325
    326        }
    327
    328        /**
    329         * Draws a grid line against the domain axis.
    330         *
    331         * @param g2  the graphics device.
    332         * @param plot  the plot.
    333         * @param dataArea  the area for plotting data (not yet adjusted for any
    334         *                  3D effect).
    335         * @param value  the Java2D value at which the grid line should be drawn.
    336         *
    337         */
    338        public void drawDomainGridline(Graphics2D g2,
    339                                       CategoryPlot plot,
    340                                       Rectangle2D dataArea,
    341                                       double value) {
    342
    343            Line2D line1 = null;
    344            Line2D line2 = null;
    345            PlotOrientation orientation = plot.getOrientation();
    346            if (orientation == PlotOrientation.HORIZONTAL) {
    347                double y0 = value;
    348                double y1 = value - getYOffset();
    349                double x0 = dataArea.getMinX();
    350                double x1 = x0 + getXOffset();
    351                double x2 = dataArea.getMaxX();
    352                line1 = new Line2D.Double(x0, y0, x1, y1);
    353                line2 = new Line2D.Double(x1, y1, x2, y1);
    354            }
    355            else if (orientation == PlotOrientation.VERTICAL) {
    356                double x0 = value;
    357                double x1 = value + getXOffset();
    358                double y0 = dataArea.getMaxY();
    359                double y1 = y0 - getYOffset();
    360                double y2 = dataArea.getMinY();
    361                line1 = new Line2D.Double(x0, y0, x1, y1);
    362                line2 = new Line2D.Double(x1, y1, x1, y2);
    363            }
    364            g2.setPaint(plot.getDomainGridlinePaint());
    365            g2.setStroke(plot.getDomainGridlineStroke());
    366            g2.draw(line1);
    367            g2.draw(line2);
    368
    369        }
    370
    371        /**
    372         * Draws a grid line against the range axis.
    373         *
    374         * @param g2  the graphics device.
    375         * @param plot  the plot.
    376         * @param axis  the value axis.
    377         * @param dataArea  the area for plotting data (not yet adjusted for any
    378         *                  3D effect).
    379         * @param value  the value at which the grid line should be drawn.
    380         *
    381         */
    382        public void drawRangeGridline(Graphics2D g2,
    383                                      CategoryPlot plot,
    384                                      ValueAxis axis,
    385                                      Rectangle2D dataArea,
    386                                      double value) {
    387
    388            Range range = axis.getRange();
    389
    390            if (!range.contains(value)) {
    391                return;
    392            }
    393
    394            Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
    395                    dataArea.getY() + getYOffset(),
    396                    dataArea.getWidth() - getXOffset(),
    397                    dataArea.getHeight() - getYOffset());
    398
    399            Line2D line1 = null;
    400            Line2D line2 = null;
    401            PlotOrientation orientation = plot.getOrientation();
    402            if (orientation == PlotOrientation.HORIZONTAL) {
    403                double x0 = axis.valueToJava2D(value, adjusted,
    404                        plot.getRangeAxisEdge());
    405                double x1 = x0 + getXOffset();
    406                double y0 = dataArea.getMaxY();
    407                double y1 = y0 - getYOffset();
    408                double y2 = dataArea.getMinY();
    409                line1 = new Line2D.Double(x0, y0, x1, y1);
    410                line2 = new Line2D.Double(x1, y1, x1, y2);
    411            }
    412            else if (orientation == PlotOrientation.VERTICAL) {
    413                double y0 = axis.valueToJava2D(value, adjusted,
    414                        plot.getRangeAxisEdge());
    415                double y1 = y0 - getYOffset();
    416                double x0 = dataArea.getMinX();
    417                double x1 = x0 + getXOffset();
    418                double x2 = dataArea.getMaxX();
    419                line1 = new Line2D.Double(x0, y0, x1, y1);
    420                line2 = new Line2D.Double(x1, y1, x2, y1);
    421            }
    422            g2.setPaint(plot.getRangeGridlinePaint());
    423            g2.setStroke(plot.getRangeGridlineStroke());
    424            g2.draw(line1);
    425            g2.draw(line2);
    426
    427        }
    428
    429        /**
    430         * Draws a range marker.
    431         *
    432         * @param g2  the graphics device.
    433         * @param plot  the plot.
    434         * @param axis  the value axis.
    435         * @param marker  the marker.
    436         * @param dataArea  the area for plotting data (not including 3D effect).
    437         */
    438        public void drawRangeMarker(Graphics2D g2,
    439                                    CategoryPlot plot,
    440                                    ValueAxis axis,
    441                                    Marker marker,
    442                                    Rectangle2D dataArea) {
    443
    444            Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
    445                    dataArea.getY() + getYOffset(),
    446                    dataArea.getWidth() - getXOffset(),
    447                    dataArea.getHeight() - getYOffset());
    448
    449            if (marker instanceof ValueMarker) {
    450                ValueMarker vm = (ValueMarker) marker;
    451                double value = vm.getValue();
    452                Range range = axis.getRange();
    453                if (!range.contains(value)) {
    454                    return;
    455                }
    456
    457                GeneralPath path = null;
    458                PlotOrientation orientation = plot.getOrientation();
    459                if (orientation == PlotOrientation.HORIZONTAL) {
    460                    float x = (float) axis.valueToJava2D(value, adjusted,
    461                            plot.getRangeAxisEdge());
    462                    float y = (float) adjusted.getMaxY();
    463                    path = new GeneralPath();
    464                    path.moveTo(x, y);
    465                    path.lineTo((float) (x + getXOffset()),
    466                            y - (float) getYOffset());
    467                    path.lineTo((float) (x + getXOffset()),
    468                            (float) (adjusted.getMinY() - getYOffset()));
    469                    path.lineTo(x, (float) adjusted.getMinY());
    470                    path.closePath();
    471                }
    472                else if (orientation == PlotOrientation.VERTICAL) {
    473                    float y = (float) axis.valueToJava2D(value, adjusted,
    474                            plot.getRangeAxisEdge());
    475                    float x = (float) dataArea.getX();
    476                    path = new GeneralPath();
    477                    path.moveTo(x, y);
    478                    path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset);
    479                    path.lineTo((float) (adjusted.getMaxX() + this.xOffset),
    480                            y - (float) this.yOffset);
    481                    path.lineTo((float) (adjusted.getMaxX()), y);
    482                    path.closePath();
    483                }
    484                g2.setPaint(marker.getPaint());
    485                g2.fill(path);
    486                g2.setPaint(marker.getOutlinePaint());
    487                g2.draw(path);
    488            }
    489            else {
    490                super.drawRangeMarker(g2, plot, axis, marker, adjusted);
    491                // TODO: draw the interval marker with a 3D effect
    492            }
    493        }
    494
    495       /**
    496         * Draw a single data item.
    497         *
    498         * @param g2  the graphics device.
    499         * @param state  the renderer state.
    500         * @param dataArea  the area in which the data is drawn.
    501         * @param plot  the plot.
    502         * @param domainAxis  the domain axis.
    503         * @param rangeAxis  the range axis.
    504         * @param dataset  the dataset.
    505         * @param row  the row index (zero-based).
    506         * @param column  the column index (zero-based).
    507         * @param pass  the pass index.
    508         */
    509        public void drawItem(Graphics2D g2,
    510                             CategoryItemRendererState state,
    511                             Rectangle2D dataArea,
    512                             CategoryPlot plot,
    513                             CategoryAxis domainAxis,
    514                             ValueAxis rangeAxis,
    515                             CategoryDataset dataset,
    516                             int row,
    517                             int column,
    518                             int pass) {
    519
    520            if (!getItemVisible(row, column)) {
    521                return;
    522            }
    523
    524            // nothing is drawn for null...
    525            Number v = dataset.getValue(row, column);
    526            if (v == null) {
    527                return;
    528            }
    529
    530            Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
    531                    dataArea.getY() + getYOffset(),
    532                    dataArea.getWidth() - getXOffset(),
    533                    dataArea.getHeight() - getYOffset());
    534
    535            PlotOrientation orientation = plot.getOrientation();
    536
    537            // current data point...
    538            double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
    539                    adjusted, plot.getDomainAxisEdge());
    540            double value = v.doubleValue();
    541            double y1 = rangeAxis.valueToJava2D(value, adjusted,
    542                    plot.getRangeAxisEdge());
    543
    544            Shape shape = getItemShape(row, column);
    545            if (orientation == PlotOrientation.HORIZONTAL) {
    546                shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
    547            }
    548            else if (orientation == PlotOrientation.VERTICAL) {
    549                shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
    550            }
    551
    552            if (getItemLineVisible(row, column)) {
    553                if (column != 0) {
    554
    555                    Number previousValue = dataset.getValue(row, column - 1);
    556                    if (previousValue != null) {
    557
    558                        // previous data point...
    559                        double previous = previousValue.doubleValue();
    560                        double x0 = domainAxis.getCategoryMiddle(column - 1,
    561                                getColumnCount(), adjusted,
    562                                plot.getDomainAxisEdge());
    563                        double y0 = rangeAxis.valueToJava2D(previous, adjusted,
    564                                plot.getRangeAxisEdge());
    565
    566                        double x2 = x0 + getXOffset();
    567                        double y2 = y0 - getYOffset();
    568                        double x3 = x1 + getXOffset();
    569                        double y3 = y1 - getYOffset();
    570
    571                        GeneralPath clip = new GeneralPath();
    572
    573                        if (orientation == PlotOrientation.HORIZONTAL) {
    574                            clip.moveTo((float) y0, (float) x0);
    575                            clip.lineTo((float) y1, (float) x1);
    576                            clip.lineTo((float) y3, (float) x3);
    577                            clip.lineTo((float) y2, (float) x2);
    578                            clip.lineTo((float) y0, (float) x0);
    579                            clip.closePath();
    580                        }
    581                        else if (orientation == PlotOrientation.VERTICAL) {
    582                            clip.moveTo((float) x0, (float) y0);
    583                            clip.lineTo((float) x1, (float) y1);
    584                            clip.lineTo((float) x3, (float) y3);
    585                            clip.lineTo((float) x2, (float) y2);
    586                            clip.lineTo((float) x0, (float) y0);
    587                            clip.closePath();
    588                        }
    589
    590                        g2.setPaint(getItemPaint(row, column));
    591                        g2.fill(clip);
    592                        g2.setStroke(getItemOutlineStroke(row, column));
    593                        g2.setPaint(getItemOutlinePaint(row, column));
    594                        g2.draw(clip);
    595                    }
    596                }
    597            }
    598
    599            // draw the item label if there is one...
    600            if (isItemLabelVisible(row, column)) {
    601                drawItemLabel(g2, orientation, dataset, row, column, x1, y1,
    602                        (value < 0.0));
    603            }
    604
    605            // add an item entity, if this information is being collected
    606            EntityCollection entities = state.getEntityCollection();
    607            if (entities != null) {
    608                addItemEntity(entities, dataset, row, column, shape);
    609            }
    610
    611        }
    612
    613        /**
    614         * Checks this renderer for equality with an arbitrary object.
    615         *
    616         * @param obj  the object (<code>null</code> permitted).
    617         *
    618         * @return A boolean.
    619         */
    620        public boolean equals(Object obj) {
    621            if (obj == this) {
    622                return true;
    623            }
    624            if (!(obj instanceof LineRenderer3D)) {
    625                return false;
    626            }
    627            LineRenderer3D that = (LineRenderer3D) obj;
    628            if (this.xOffset != that.xOffset) {
    629                return false;
    630            }
    631            if (this.yOffset != that.yOffset) {
    632                return false;
    633            }
    634            if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
    635                return false;
    636            }
    637            return super.equals(obj);
    638        }
    639
    640        /**
    641         * Provides serialization support.
    642         *
    643         * @param stream  the output stream.
    644         *
    645         * @throws IOException  if there is an I/O error.
    646         */
    647        private void writeObject(ObjectOutputStream stream) throws IOException {
    648            stream.defaultWriteObject();
    649            SerialUtilities.writePaint(this.wallPaint, stream);
    650        }
    651
    652        /**
    653         * Provides serialization support.
    654         *
    655         * @param stream  the input stream.
    656         *
    657         * @throws IOException  if there is an I/O error.
    658         * @throws ClassNotFoundException  if there is a classpath problem.
    659         */
    660        private void readObject(ObjectInputStream stream)
    661                throws IOException, ClassNotFoundException {
    662            stream.defaultReadObject();
    663            this.wallPaint = SerialUtilities.readPaint(stream);
    664        }
    665
    666    }