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
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}