    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jfreechart/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * ---------------------------
    028 * 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
    051import java.awt.BasicStroke;
    052import java.awt.Color;
    053import java.awt.Paint;
    054import java.awt.Polygon;
    055import java.awt.Shape;
    056import java.awt.Stroke;
    057import java.awt.geom.Ellipse2D;
    058import java.awt.geom.Rectangle2D;
    059import java.io.IOException;
    060import java.io.ObjectInputStream;
    061import java.io.ObjectOutputStream;
    062import java.io.Serializable;
    063import java.util.Arrays;
    064
    065import org.jfree.chart.ChartColor;
    066import org.jfree.io.SerialUtilities;
    067import org.jfree.util.PublicCloneable;
    068import 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 */
    075public 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}