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    }