Source for org.jfree.chart.plot.DefaultDrawingSupplier

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------------------------
  28:  * DefaultDrawingSupplier.java
  29:  * ---------------------------
  30:  * (C) Copyright 2003-2007, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Jeremy Bowman;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 16-Jan-2003 : Version 1 (DG);
  38:  * 17-Jan-2003 : Added stroke method, renamed DefaultPaintSupplier 
  39:  *               --> DefaultDrawingSupplier (DG)
  40:  * 27-Jan-2003 : Incorporated code from SeriesShapeFactory, originally 
  41:  *               contributed by Jeremy Bowman (DG);
  42:  * 25-Mar-2003 : Implemented Serializable (DG);
  43:  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
  44:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  45:  * 13-Jun-2007 : Added fillPaintSequence (DG);
  46:  *
  47:  */
  48: 
  49:  package org.jfree.chart.plot;
  50: 
  51: import java.awt.BasicStroke;
  52: import java.awt.Color;
  53: import java.awt.Paint;
  54: import java.awt.Polygon;
  55: import java.awt.Shape;
  56: import java.awt.Stroke;
  57: import java.awt.geom.Ellipse2D;
  58: import java.awt.geom.Rectangle2D;
  59: import java.io.IOException;
  60: import java.io.ObjectInputStream;
  61: import java.io.ObjectOutputStream;
  62: import java.io.Serializable;
  63: import java.util.Arrays;
  64: 
  65: import org.jfree.chart.ChartColor;
  66: import org.jfree.io.SerialUtilities;
  67: import org.jfree.util.PublicCloneable;
  68: import org.jfree.util.ShapeUtilities;
  69: 
  70: /**
  71:  * A default implementation of the {@link DrawingSupplier} interface.  All
  72:  * {@link Plot} instances have a new instance of this class installed by 
  73:  * default.
  74:  */
  75: public class DefaultDrawingSupplier implements DrawingSupplier, Cloneable, 
  76:         PublicCloneable, Serializable {
  77: 
  78:     /** For serialization. */
  79:     private static final long serialVersionUID = -7339847061039422538L;
  80:     
  81:     /** The default fill paint sequence. */
  82:     public static final Paint[] DEFAULT_PAINT_SEQUENCE 
  83:             = ChartColor.createDefaultPaintArray();
  84: 
  85:     /** The default outline paint sequence. */
  86:     public static final Paint[] DEFAULT_OUTLINE_PAINT_SEQUENCE = new Paint[] {
  87:             Color.lightGray};
  88: 
  89:     /** The default fill paint sequence. */
  90:     public static final Paint[] DEFAULT_FILL_PAINT_SEQUENCE = new Paint[] {
  91:             Color.white};
  92: 
  93:     /** The default stroke sequence. */
  94:     public static final Stroke[] DEFAULT_STROKE_SEQUENCE = new Stroke[] {
  95:             new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, 
  96:                     BasicStroke.JOIN_BEVEL)};
  97: 
  98:     /** The default outline stroke sequence. */
  99:     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: }