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    }