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