    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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     * DefaultDrawingSupplier.java
    029     * ---------------------------
    030     * (C) Copyright 2003, 2004, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Jeremy Bowman;
    034     *
    035     * $Id: DefaultDrawingSupplier.java,v 1.6.2.1 2005/10/25 20:52:07 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 16-Jan-2003 : Version 1 (DG);
    040     * 17-Jan-2003 : Added stroke method, renamed DefaultPaintSupplier
    041     *               --> DefaultDrawingSupplier (DG)
    042     * 27-Jan-2003 : Incorporated code from SeriesShapeFactory, originally
    043     *               contributed by Jeremy Bowman (DG);
    044     * 25-Mar-2003 : Implemented Serializable (DG);
    045     * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
    046     *
    047     */
    048
    049     package org.jfree.chart.plot;
    050
    051    import java.awt.BasicStroke;
    052    import java.awt.Color;
    053    import java.awt.Paint;
    054    import java.awt.Polygon;
    055    import java.awt.Shape;
    056    import java.awt.Stroke;
    057    import java.awt.geom.Ellipse2D;
    058    import java.awt.geom.Rectangle2D;
    059    import java.io.IOException;
    060    import java.io.ObjectInputStream;
    061    import java.io.ObjectOutputStream;
    062    import java.io.Serializable;
    063    import java.util.Arrays;
    064
    065    import org.jfree.chart.ChartColor;
    066    import org.jfree.io.SerialUtilities;
    067    import org.jfree.util.PublicCloneable;
    068    import org.jfree.util.ShapeUtilities;
    069
    070    /**
    071     * A default implementation of the {@link DrawingSupplier} interface.
    072     *
    073     */
    074    public class DefaultDrawingSupplier implements DrawingSupplier,
    075                                                   Cloneable,
    076                                                   PublicCloneable,
    077                                                   Serializable {
    078
    079        /** For serialization. */
    080        private static final long serialVersionUID = -7339847061039422538L;
    081
    082        /** The default fill paint sequence. */
    083        public static final Paint[] DEFAULT_PAINT_SEQUENCE
    084            = ChartColor.createDefaultPaintArray();
    085
    086        /** The default outline paint sequence. */
    087        public static final Paint[] DEFAULT_OUTLINE_PAINT_SEQUENCE = new Paint[] {
    088                                        Color.lightGray
    089                                    };
    090
    091        /** The default stroke sequence. */
    092        public static final Stroke[] DEFAULT_STROKE_SEQUENCE = new Stroke[] {
    093                                        new BasicStroke(1.0f,
    094                                                        BasicStroke.CAP_SQUARE,
    095                                                        BasicStroke.JOIN_BEVEL)
    096                                    };
    097
    098        /** The default outline stroke sequence. */
    099        public static final Stroke[] DEFAULT_OUTLINE_STROKE_SEQUENCE
    100            = new Stroke[] {
    101                new BasicStroke(
    102                    1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL
    103                )
    104            };
    105
    106        /** The default shape sequence. */
    107        public static final Shape[] DEFAULT_SHAPE_SEQUENCE
    108            = createStandardSeriesShapes();
    109
    110        /** The paint sequence. */
    111        private transient Paint[] paintSequence;
    112
    113        /** The current paint index. */
    114        private int paintIndex;
    115
    116        /** The outline paint sequence. */
    117        private transient Paint[] outlinePaintSequence;
    118
    119        /** The current outline paint index. */
    120        private int outlinePaintIndex;
    121
    122        /** The stroke sequence. */
    123        private transient Stroke[] strokeSequence;
    124
    125        /** The current stroke index. */
    126        private int strokeIndex;
    127
    128        /** The outline stroke sequence. */
    129        private transient Stroke[] outlineStrokeSequence;
    130
    131        /** The current outline stroke index. */
    132        private int outlineStrokeIndex;
    133
    134        /** The shape sequence. */
    135        private transient Shape[] shapeSequence;
    136
    137        /** The current shape index. */
    138        private int shapeIndex;
    139
    140        /**
    141         * Creates a new supplier, with default sequences for fill paint, outline
    142         * paint, stroke and shapes.
    143         */
    144        public DefaultDrawingSupplier() {
    145
    146            this(DEFAULT_PAINT_SEQUENCE,
    147                 DEFAULT_OUTLINE_PAINT_SEQUENCE,
    148                 DEFAULT_STROKE_SEQUENCE,
    149                 DEFAULT_OUTLINE_STROKE_SEQUENCE,
    150                 DEFAULT_SHAPE_SEQUENCE);
    151
    152        }
    153
    154        /**
    155         * Creates a new supplier.
    156         *
    157         * @param paintSequence  the fill paint sequence.
    158         * @param outlinePaintSequence  the outline paint sequence.
    159         * @param strokeSequence  the stroke sequence.
    160         * @param outlineStrokeSequence  the outline stroke sequence.
    161         * @param shapeSequence  the shape sequence.
    162         */
    163        public DefaultDrawingSupplier(Paint[] paintSequence,
    164                                      Paint[] outlinePaintSequence,
    165                                      Stroke[] strokeSequence,
    166                                      Stroke[] outlineStrokeSequence,
    167                                      Shape[] shapeSequence) {
    168
    169            this.paintSequence = paintSequence;
    170            this.outlinePaintSequence = outlinePaintSequence;
    171            this.strokeSequence = strokeSequence;
    172            this.outlineStrokeSequence = outlineStrokeSequence;
    173            this.shapeSequence = shapeSequence;
    174
    175        }
    176
    177        /**
    178         * Returns the next paint in the sequence.
    179         *
    180         * @return The paint.
    181         */
    182        public Paint getNextPaint() {
    183            Paint result
    184                = this.paintSequence[this.paintIndex % this.paintSequence.length];
    185            this.paintIndex++;
    186            return result;
    187        }
    188
    189        /**
    190         * Returns the next outline paint in the sequence.
    191         *
    192         * @return The paint.
    193         */
    194        public Paint getNextOutlinePaint() {
    195            Paint result = this.outlinePaintSequence[
    196                this.outlinePaintIndex % this.outlinePaintSequence.length
    197            ];
    198            this.outlinePaintIndex++;
    199            return result;
    200        }
    201
    202        /**
    203         * Returns the next stroke in the sequence.
    204         *
    205         * @return The stroke.
    206         */
    207        public Stroke getNextStroke() {
    208            Stroke result = this.strokeSequence[
    209                this.strokeIndex % this.strokeSequence.length
    210            ];
    211            this.strokeIndex++;
    212            return result;
    213        }
    214
    215        /**
    216         * Returns the next outline stroke in the sequence.
    217         *
    218         * @return The stroke.
    219         */
    220        public Stroke getNextOutlineStroke() {
    221            Stroke result = this.outlineStrokeSequence[
    222                this.outlineStrokeIndex % this.outlineStrokeSequence.length
    223            ];
    224            this.outlineStrokeIndex++;
    225            return result;
    226        }
    227
    228        /**
    229         * Returns the next shape in the sequence.
    230         *
    231         * @return The shape.
    232         */
    233        public Shape getNextShape() {
    234            Shape result = this.shapeSequence[
    235                this.shapeIndex % this.shapeSequence.length
    236            ];
    237            this.shapeIndex++;
    238            return result;
    239        }
    240
    241        /**
    242         * Creates an array of standard shapes to display for the items in series
    243         * on charts.
    244         *
    245         * @return The array of shapes.
    246         */
    247        public static Shape[] createStandardSeriesShapes() {
    248
    249            Shape[] result = new Shape[10];
    250
    251            double size = 6.0;
    252            double delta = size / 2.0;
    253            int[] xpoints = null;
    254            int[] ypoints = null;
    255
    256            // square
    257            result[0] = new Rectangle2D.Double(-delta, -delta, size, size);
    258            // circle
    259            result[1] = new Ellipse2D.Double(-delta, -delta, size, size);
    260
    261            // up-pointing triangle
    262            xpoints = intArray(0.0, delta, -delta);
    263            ypoints = intArray(-delta, delta, delta);
    264            result[2] = new Polygon(xpoints, ypoints, 3);
    265
    266            // diamond
    267            xpoints = intArray(0.0, delta, 0.0, -delta);
    268            ypoints = intArray(-delta, 0.0, delta, 0.0);
    269            result[3] = new Polygon(xpoints, ypoints, 4);
    270
    271            // horizontal rectangle
    272            result[4] = new Rectangle2D.Double(-delta, -delta / 2, size, size / 2);
    273
    274            // down-pointing triangle
    275            xpoints = intArray(-delta, +delta, 0.0);
    276            ypoints = intArray(-delta, -delta, delta);
    277            result[5] = new Polygon(xpoints, ypoints, 3);
    278
    279            // horizontal ellipse
    280            result[6] = new Ellipse2D.Double(-delta, -delta / 2, size, size / 2);
    281
    282            // right-pointing triangle
    283            xpoints = intArray(-delta, delta, -delta);
    284            ypoints = intArray(-delta, 0.0, delta);
    285            result[7] = new Polygon(xpoints, ypoints, 3);
    286
    287            // vertical rectangle
    288            result[8] = new Rectangle2D.Double(-delta / 2, -delta, size / 2, size);
    289
    290            // left-pointing triangle
    291            xpoints = intArray(-delta, delta, delta);
    292            ypoints = intArray(0.0, -delta, +delta);
    293            result[9] = new Polygon(xpoints, ypoints, 3);
    294
    295            return result;
    296
    297        }
    298
    299        /**
    300         * Tests this object for equality with another object.
    301         *
    302         * @param obj  the object (<code>null</code> permitted).
    303         *
    304         * @return A boolean.
    305         */
    306        public boolean equals(Object obj) {
    307
    308            if (obj == this) {
    309                return true;
    310            }
    311
    312            if (!(obj instanceof DefaultDrawingSupplier)) {
    313                return false;
    314            }
    315
    316            DefaultDrawingSupplier that = (DefaultDrawingSupplier) obj;
    317
    318            if (!Arrays.equals(this.paintSequence, that.paintSequence)) {
    319                return false;
    320            }
    321            if (this.paintIndex != that.paintIndex) {
    322                return false;
    323            }
    324            if (!Arrays.equals(this.outlinePaintSequence,
    325                    that.outlinePaintSequence)) {
    326                return false;
    327            }
    328            if (this.outlinePaintIndex != that.outlinePaintIndex) {
    329                return false;
    330            }
    331            if (!Arrays.equals(this.strokeSequence, that.strokeSequence)) {
    332                return false;
    333            }
    334            if (this.strokeIndex != that.strokeIndex) {
    335                return false;
    336            }
    337            if (!Arrays.equals(this.outlineStrokeSequence,
    338                    that.outlineStrokeSequence)) {
    339                return false;
    340            }
    341            if (this.outlineStrokeIndex != that.outlineStrokeIndex) {
    342                return false;
    343            }
    344            if (!equalShapes(this.shapeSequence, that.shapeSequence)) {
    345                return false;
    346            }
    347            if (this.shapeIndex != that.shapeIndex) {
    348                return false;
    349            }
    350            return true;
    351
    352        }
    353
    354        /**
    355         * A utility method for testing the equality of two arrays of shapes.
    356         *
    357         * @param s1  the first array (<code>null</code> permitted).
    358         * @param s2  the second array (<code>null</code> permitted).
    359         *
    360         * @return A boolean.
    361         */
    362        private boolean equalShapes(Shape[] s1, Shape[] s2) {
    363            if (s1 == null) {
    364                return s2 == null;
    365            }
    366            if (s2 == null) {
    367                return false;
    368            }
    369            if (s1.length != s2.length) {
    370                return false;
    371            }
    372            for (int i = 0; i < s1.length; i++) {
    373                if (!ShapeUtilities.equal(s1[i], s2[i])) {
    374                    return false;
    375                }
    376            }
    377            return true;
    378        }
    379
    380        /**
    381         * Handles serialization.
    382         *
    383         * @param stream  the output stream.
    384         *
    385         * @throws IOException if there is an I/O problem.
    386         */
    387        private void writeObject(ObjectOutputStream stream) throws IOException {
    388            stream.defaultWriteObject();
    389
    390            int paintCount = this.paintSequence.length;
    391            stream.writeInt(paintCount);
    392            for (int i = 0; i < paintCount; i++) {
    393                SerialUtilities.writePaint(this.paintSequence[i], stream);
    394            }
    395
    396            int outlinePaintCount = this.outlinePaintSequence.length;
    397            stream.writeInt(outlinePaintCount);
    398            for (int i = 0; i < outlinePaintCount; i++) {
    399                SerialUtilities.writePaint(this.outlinePaintSequence[i], stream);
    400            }
    401
    402            int strokeCount = this.strokeSequence.length;
    403            stream.writeInt(strokeCount);
    404            for (int i = 0; i < strokeCount; i++) {
    405                SerialUtilities.writeStroke(this.strokeSequence[i], stream);
    406            }
    407
    408            int outlineStrokeCount = this.outlineStrokeSequence.length;
    409            stream.writeInt(outlineStrokeCount);
    410            for (int i = 0; i < outlineStrokeCount; i++) {
    411                SerialUtilities.writeStroke(this.outlineStrokeSequence[i], stream);
    412            }
    413
    414            int shapeCount = this.shapeSequence.length;
    415            stream.writeInt(shapeCount);
    416            for (int i = 0; i < shapeCount; i++) {
    417                SerialUtilities.writeShape(this.shapeSequence[i], stream);
    418            }
    419
    420        }
    421
    422        /**
    423         * Restores a serialized object.
    424         *
    425         * @param stream  the input stream.
    426         *
    427         * @throws IOException if there is an I/O problem.
    428         * @throws ClassNotFoundException if there is a problem loading a class.
    429         */
    430        private void readObject(ObjectInputStream stream)
    431            throws IOException, ClassNotFoundException {
    432            stream.defaultReadObject();
    433
    434            int paintCount = stream.readInt();
    435            this.paintSequence = new Paint[paintCount];
    436            for (int i = 0; i < paintCount; i++) {
    437                this.paintSequence[i] = SerialUtilities.readPaint(stream);
    438            }
    439
    440            int outlinePaintCount = stream.readInt();
    441            this.outlinePaintSequence = new Paint[outlinePaintCount];
    442            for (int i = 0; i < outlinePaintCount; i++) {
    443                this.outlinePaintSequence[i] = SerialUtilities.readPaint(stream);
    444            }
    445
    446            int strokeCount = stream.readInt();
    447            this.strokeSequence = new Stroke[strokeCount];
    448            for (int i = 0; i < strokeCount; i++) {
    449                this.strokeSequence[i] = SerialUtilities.readStroke(stream);
    450            }
    451
    452            int outlineStrokeCount = stream.readInt();
    453            this.outlineStrokeSequence = new Stroke[outlineStrokeCount];
    454            for (int i = 0; i < outlineStrokeCount; i++) {
    455                this.outlineStrokeSequence[i] = SerialUtilities.readStroke(stream);
    456            }
    457
    458            int shapeCount = stream.readInt();
    459            this.shapeSequence = new Shape[shapeCount];
    460            for (int i = 0; i < shapeCount; i++) {
    461                this.shapeSequence[i] = SerialUtilities.readShape(stream);
    462            }
    463
    464        }
    465
    466        /**
    467         * Helper method to avoid lots of explicit casts in getShape().  Returns
    468         * an array containing the provided doubles cast to ints.
    469         *
    470         * @param a  x
    471         * @param b  y
    472         * @param c  z
    473         *
    474         * @return int[3] with converted params.
    475         */
    476        private static int[] intArray(double a, double b, double c) {
    477            return new int[] {(int) a, (int) b, (int) c};
    478        }
    479
    480        /**
    481         * Helper method to avoid lots of explicit casts in getShape().  Returns
    482         * an array containing the provided doubles cast to ints.
    483         *
    484         * @param a  x
    485         * @param b  y
    486         * @param c  z
    487         * @param d  t
    488         *
    489         * @return int[3] with converted params.
    490         */
    491        private static int[] intArray(double a, double b, double c, double d) {
    492            return new int[] {(int) a, (int) b, (int) c, (int) d};
    493        }
    494
    495        /**
    496         * Returns a clone.
    497         *
    498         * @return A clone.
    499         *
    500         * @throws CloneNotSupportedException if a component of the supplier does
    501         *                                    not support cloning.
    502         */
    503        public Object clone() throws CloneNotSupportedException {
    504            DefaultDrawingSupplier clone = (DefaultDrawingSupplier) super.clone();
    505            return clone;
    506        }
    507    }