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