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