KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > xy > CandlestickRenderer


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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  * CandlestickRenderer.java
29  * ------------------------
30  * (C) Copyright 2001-2006, by Object Refinery Limited.
31  *
32  * Original Authors: David Gilbert (for Object Refinery Limited);
33  * Sylvain Vieujot;
34  * Contributor(s): Richard Atkinson;
35  * Christian W. Zuckschwerdt;
36  * Jerome Fisher;
37  *
38  * $Id: CandlestickRenderer.java,v 1.7.2.3 2006/08/17 09:21:25 mungady Exp $
39  *
40  * Changes
41  * -------
42  * 13-Dec-2001 : Version 1. Based on code in the (now redundant)
43  * CandlestickPlot class, written by Sylvain Vieujot (DG);
44  * 23-Jan-2002 : Added DrawInfo parameter to drawItem() method (DG);
45  * 28-Mar-2002 : Added a property change listener mechanism so that renderers
46  * no longer need to be immutable. Added properties for up and
47  * down colors (DG);
48  * 04-Apr-2002 : Updated with new automatic width calculation and optional
49  * volume display, contributed by Sylvain Vieujot (DG);
50  * 09-Apr-2002 : Removed translatedRangeZero from the drawItem() method, and
51  * changed the return type of the drawItem method to void,
52  * reflecting a change in the XYItemRenderer interface. Added
53  * tooltip code to drawItem() method (DG);
54  * 25-Jun-2002 : Removed redundant code (DG);
55  * 05-Aug-2002 : Small modification to drawItem method to support URLs for HTML
56  * image maps (RA);
57  * 19-Sep-2002 : Fixed errors reported by Checkstyle (DG);
58  * 25-Mar-2003 : Implemented Serializable (DG);
59  * 01-May-2003 : Modified drawItem() method signature (DG);
60  * 30-Jun-2003 : Added support for PlotOrientation (for completeness, this
61  * renderer is unlikely to be used with a HORIZONTAL
62  * orientation) (DG);
63  * 30-Jul-2003 : Modified entity constructor (CZ);
64  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
65  * 29-Aug-2003 : Moved maxVolume calculation to initialise method (see bug
66  * report 796619) (DG);
67  * 02-Sep-2003 : Added maxCandleWidthInMilliseconds as workaround for bug
68  * 796621 (DG);
69  * 08-Sep-2003 : Changed ValueAxis API (DG);
70  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
71  * 13-Oct-2003 : Applied patch from Jerome Fisher to improve auto width
72  * calculations (DG);
73  * 23-Dec-2003 : Fixed bug where up and down paint are used incorrectly (DG);
74  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
75  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
76  * getYValue() (DG);
77  * ------------- JFREECHART 1.0.0 ---------------------------------------------
78  * 06-Jul-2006 : Swapped calls to getX() --> getXValue(), and the same for the
79  * other data values (DG);
80  * 17-Aug-2006 : Corrections to the equals() method (DG);
81  *
82  */

83
84 package org.jfree.chart.renderer.xy;
85
86 import java.awt.AlphaComposite JavaDoc;
87 import java.awt.Color JavaDoc;
88 import java.awt.Composite JavaDoc;
89 import java.awt.Graphics2D JavaDoc;
90 import java.awt.Paint JavaDoc;
91 import java.awt.Shape JavaDoc;
92 import java.awt.Stroke JavaDoc;
93 import java.awt.geom.Line2D JavaDoc;
94 import java.awt.geom.Rectangle2D JavaDoc;
95 import java.io.IOException JavaDoc;
96 import java.io.ObjectInputStream JavaDoc;
97 import java.io.ObjectOutputStream JavaDoc;
98 import java.io.Serializable JavaDoc;
99
100 import org.jfree.chart.axis.ValueAxis;
101 import org.jfree.chart.entity.EntityCollection;
102 import org.jfree.chart.entity.XYItemEntity;
103 import org.jfree.chart.event.RendererChangeEvent;
104 import org.jfree.chart.labels.HighLowItemLabelGenerator;
105 import org.jfree.chart.labels.XYToolTipGenerator;
106 import org.jfree.chart.plot.CrosshairState;
107 import org.jfree.chart.plot.PlotOrientation;
108 import org.jfree.chart.plot.PlotRenderingInfo;
109 import org.jfree.chart.plot.XYPlot;
110 import org.jfree.data.xy.IntervalXYDataset;
111 import org.jfree.data.xy.OHLCDataset;
112 import org.jfree.data.xy.XYDataset;
113 import org.jfree.io.SerialUtilities;
114 import org.jfree.ui.RectangleEdge;
115 import org.jfree.util.PaintUtilities;
116 import org.jfree.util.PublicCloneable;
117
118 /**
119  * A renderer that draws candlesticks on an {@link XYPlot} (requires a
120  * {@link OHLCDataset}).
121  * <P>
122  * This renderer does not include code to calculate the crosshair point for the
123  * plot.
124  */

125 public class CandlestickRenderer extends AbstractXYItemRenderer
126                                  implements XYItemRenderer,
127                                             Cloneable JavaDoc,
128                                             PublicCloneable,
129                                             Serializable JavaDoc {
130             
131     /** For serialization. */
132     private static final long serialVersionUID = 50390395841817121L;
133     
134     /** The average width method. */
135     public static final int WIDTHMETHOD_AVERAGE = 0;
136     
137     /** The smallest width method. */
138     public static final int WIDTHMETHOD_SMALLEST = 1;
139     
140     /** The interval data method. */
141     public static final int WIDTHMETHOD_INTERVALDATA = 2;
142
143     /** The method of automatically calculating the candle width. */
144     private int autoWidthMethod = WIDTHMETHOD_AVERAGE;
145
146     /**
147      * The number (generally between 0.0 and 1.0) by which the available space
148      * automatically calculated for the candles will be multiplied to determine
149      * the actual width to use.
150      */

151     private double autoWidthFactor = 4.5 / 7;
152
153     /** The minimum gap between one candle and the next */
154     private double autoWidthGap = 0.0;
155
156     /** The candle width. */
157     private double candleWidth;
158     
159     /** The maximum candlewidth in milliseconds. */
160     private double maxCandleWidthInMilliseconds = 1000.0 * 60.0 * 60.0 * 20.0;
161     
162     /** Temporary storage for the maximum candle width. */
163     private double maxCandleWidth;
164
165     /**
166      * The paint used to fill the candle when the price moved up from open to
167      * close.
168      */

169     private transient Paint JavaDoc upPaint;
170
171     /**
172      * The paint used to fill the candle when the price moved down from open
173      * to close.
174      */

175     private transient Paint JavaDoc downPaint;
176
177     /** A flag controlling whether or not volume bars are drawn on the chart. */
178     private boolean drawVolume;
179     
180     /** Temporary storage for the maximum volume. */
181     private transient double maxVolume;
182
183     /**
184      * Creates a new renderer for candlestick charts.
185      */

186     public CandlestickRenderer() {
187         this(-1.0);
188     }
189
190     /**
191      * Creates a new renderer for candlestick charts.
192      * <P>
193      * Use -1 for the candle width if you prefer the width to be calculated
194      * automatically.
195      *
196      * @param candleWidth The candle width.
197      */

198     public CandlestickRenderer(double candleWidth) {
199         this(candleWidth, true, new HighLowItemLabelGenerator());
200     }
201
202     /**
203      * Creates a new renderer for candlestick charts.
204      * <P>
205      * Use -1 for the candle width if you prefer the width to be calculated
206      * automatically.
207      *
208      * @param candleWidth the candle width.
209      * @param drawVolume a flag indicating whether or not volume bars should
210      * be drawn.
211      * @param toolTipGenerator the tool tip generator. <code>null</code> is
212      * none.
213      */

214     public CandlestickRenderer(double candleWidth, boolean drawVolume,
215                                XYToolTipGenerator toolTipGenerator) {
216
217         super();
218         setToolTipGenerator(toolTipGenerator);
219         this.candleWidth = candleWidth;
220         this.drawVolume = drawVolume;
221         this.upPaint = Color.green;
222         this.downPaint = Color.red;
223
224     }
225
226     /**
227      * Returns the width of each candle.
228      *
229      * @return The candle width.
230      *
231      * @see #setCandleWidth(double)
232      */

233     public double getCandleWidth() {
234         return this.candleWidth;
235     }
236
237     /**
238      * Sets the candle width.
239      * <P>
240      * If you set the width to a negative value, the renderer will calculate
241      * the candle width automatically based on the space available on the chart.
242      *
243      * @param width The width.
244      * @see #setAutoWidthMethod(int)
245      * @see #setAutoWidthGap(double)
246      * @see #setAutoWidthFactor(double)
247      * @see #setMaxCandleWidthInMilliseconds(double)
248      */

249     public void setCandleWidth(double width) {
250         if (width != this.candleWidth) {
251             this.candleWidth = width;
252             notifyListeners(new RendererChangeEvent(this));
253         }
254     }
255
256     /**
257      * Returns the maximum width (in milliseconds) of each candle.
258      *
259      * @return The maximum candle width in milliseconds.
260      */

261     public double getMaxCandleWidthInMilliseconds() {
262         return this.maxCandleWidthInMilliseconds;
263     }
264
265     /**
266      * Sets the maximum candle width (in milliseconds).
267      *
268      * @param millis The maximum width.
269      * @see #setCandleWidth(double)
270      * @see #setAutoWidthMethod(int)
271      * @see #setAutoWidthGap(double)
272      * @see #setAutoWidthFactor(double)
273      */

274     public void setMaxCandleWidthInMilliseconds(double millis) {
275         this.maxCandleWidthInMilliseconds = millis;
276         notifyListeners(new RendererChangeEvent(this));
277     }
278
279     /**
280      * Returns the method of automatically calculating the candle width.
281      *
282      * @return The method of automatically calculating the candle width.
283      */

284     public int getAutoWidthMethod() {
285         return this.autoWidthMethod;
286     }
287
288     /**
289      * Sets the method of automatically calculating the candle width.
290      * <p>
291      * <code>WIDTHMETHOD_AVERAGE</code>: Divides the entire display (ignoring
292      * scale factor) by the number of items, and uses this as the available
293      * width.<br>
294      * <code>WIDTHMETHOD_SMALLEST</code>: Checks the interval between each
295      * item, and uses the smallest as the available width.<br>
296      * <code>WIDTHMETHOD_INTERVALDATA</code>: Assumes that the dataset supports
297      * the IntervalXYDataset interface, and uses the startXValue - endXValue as
298      * the available width.
299      * <br>
300      *
301      * @param autoWidthMethod The method of automatically calculating the
302      * candle width.
303      *
304      * @see #WIDTHMETHOD_AVERAGE
305      * @see #WIDTHMETHOD_SMALLEST
306      * @see #WIDTHMETHOD_INTERVALDATA
307      * @see #setCandleWidth(double)
308      * @see #setAutoWidthGap(double)
309      * @see #setAutoWidthFactor(double)
310      * @see #setMaxCandleWidthInMilliseconds(double)
311      */

312     public void setAutoWidthMethod(int autoWidthMethod) {
313         if (this.autoWidthMethod != autoWidthMethod) {
314             this.autoWidthMethod = autoWidthMethod;
315             notifyListeners(new RendererChangeEvent(this));
316         }
317     }
318
319     /**
320      * Returns the factor by which the available space automatically
321      * calculated for the candles will be multiplied to determine the actual
322      * width to use.
323      *
324      * @return The width factor (generally between 0.0 and 1.0).
325      */

326     public double getAutoWidthFactor() {
327         return this.autoWidthFactor;
328     }
329
330     /**
331      * Sets the factor by which the available space automatically calculated
332      * for the candles will be multiplied to determine the actual width to use.
333      *
334      * @param autoWidthFactor The width factor (generally between 0.0 and 1.0).
335      * @see #setCandleWidth(double)
336      * @see #setAutoWidthMethod(int)
337      * @see #setAutoWidthGap(double)
338      * @see #setMaxCandleWidthInMilliseconds(double)
339      */

340     public void setAutoWidthFactor(double autoWidthFactor) {
341         if (this.autoWidthFactor != autoWidthFactor) {
342             this.autoWidthFactor = autoWidthFactor;
343             notifyListeners(new RendererChangeEvent(this));
344         }
345     }
346
347     /**
348      * Returns the amount of space to leave on the left and right of each
349      * candle when automatically calculating widths.
350      *
351      * @return The gap.
352      */

353     public double getAutoWidthGap() {
354         return this.autoWidthGap;
355     }
356
357     /**
358      * Sets the amount of space to leave on the left and right of each candle
359      * when automatically calculating widths.
360      *
361      * @param autoWidthGap The gap.
362      * @see #setCandleWidth(double)
363      * @see #setAutoWidthMethod(int)
364      * @see #setAutoWidthFactor(double)
365      * @see #setMaxCandleWidthInMilliseconds(double)
366      */

367     public void setAutoWidthGap(double autoWidthGap) {
368         if (this.autoWidthGap != autoWidthGap) {
369             this.autoWidthGap = autoWidthGap;
370             notifyListeners(new RendererChangeEvent(this));
371         }
372     }
373
374     /**
375      * Returns the paint used to fill candles when the price moves up from open
376      * to close.
377      *
378      * @return The paint.
379      */

380     public Paint JavaDoc getUpPaint() {
381         return this.upPaint;
382     }
383
384     /**
385      * Sets the paint used to fill candles when the price moves up from open
386      * to close.
387      * <P>
388      * Registered property change listeners are notified that the
389      * "CandleStickRenderer.upPaint" property has changed.
390      *
391      * @param paint The paint.
392      */

393     public void setUpPaint(Paint JavaDoc paint) {
394         this.upPaint = paint;
395         notifyListeners(new RendererChangeEvent(this));
396     }
397
398     /**
399      * Returns the paint used to fill candles when the price moves down from
400      * open to close.
401      *
402      * @return The paint.
403      */

404     public Paint JavaDoc getDownPaint() {
405         return this.downPaint;
406     }
407
408     /**
409      * Sets the paint used to fill candles when the price moves down from open
410      * to close.
411      * <P>
412      * Registered property change listeners are notified that the
413      * "CandleStickRenderer.downPaint" property has changed.
414      *
415      * @param paint The paint.
416      */

417     public void setDownPaint(Paint JavaDoc paint) {
418         this.downPaint = paint;
419         notifyListeners(new RendererChangeEvent(this));
420     }
421
422     /**
423      * Returns a flag indicating whether or not volume bars are drawn on the
424      * chart.
425      *
426      * @return <code>true</code> if volume bars are drawn on the chart.
427      */

428     public boolean drawVolume() {
429         return this.drawVolume;
430     }
431
432     /**
433      * Sets a flag that controls whether or not volume bars are drawn in the
434      * background.
435      *
436      * @param flag The flag.
437      */

438     public void setDrawVolume(boolean flag) {
439         if (this.drawVolume != flag) {
440             this.drawVolume = flag;
441             notifyListeners(new RendererChangeEvent(this));
442         }
443     }
444
445     /**
446      * Initialises the renderer then returns the number of 'passes' through the
447      * data that the renderer will require (usually just one). This method
448      * will be called before the first item is rendered, giving the renderer
449      * an opportunity to initialise any state information it wants to maintain.
450      * The renderer can do nothing if it chooses.
451      *
452      * @param g2 the graphics device.
453      * @param dataArea the area inside the axes.
454      * @param plot the plot.
455      * @param dataset the data.
456      * @param info an optional info collection object to return data back to
457      * the caller.
458      *
459      * @return The number of passes the renderer requires.
460      */

461     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
462                                           Rectangle2D JavaDoc dataArea,
463                                           XYPlot plot,
464                                           XYDataset dataset,
465                                           PlotRenderingInfo info) {
466           
467         // calculate the maximum allowed candle width from the axis...
468
ValueAxis axis = plot.getDomainAxis();
469         double x1 = axis.getLowerBound();
470         double x2 = x1 + this.maxCandleWidthInMilliseconds;
471         RectangleEdge edge = plot.getDomainAxisEdge();
472         double xx1 = axis.valueToJava2D(x1, dataArea, edge);
473         double xx2 = axis.valueToJava2D(x2, dataArea, edge);
474         this.maxCandleWidth = Math.abs(xx2 - xx1);
475             // Absolute value, since the relative x
476
// positions are reversed for horizontal orientation
477

478         // calculate the highest volume in the dataset...
479
if (this.drawVolume) {
480             OHLCDataset highLowDataset = (OHLCDataset) dataset;
481             this.maxVolume = 0.0;
482             for (int series = 0; series < highLowDataset.getSeriesCount();
483                  series++) {
484                 for (int item = 0; item < highLowDataset.getItemCount(series);
485                      item++) {
486                     double volume = highLowDataset.getVolumeValue(series, item);
487                     if (volume > this.maxVolume) {
488                         this.maxVolume = volume;
489                     }
490                     
491                 }
492             }
493         }
494         
495         return new XYItemRendererState(info);
496     }
497
498     /**
499      * Draws the visual representation of a single data item.
500      *
501      * @param g2 the graphics device.
502      * @param state the renderer state.
503      * @param dataArea the area within which the plot is being drawn.
504      * @param info collects info about the drawing.
505      * @param plot the plot (can be used to obtain standard color
506      * information etc).
507      * @param domainAxis the domain axis.
508      * @param rangeAxis the range axis.
509      * @param dataset the dataset.
510      * @param series the series index (zero-based).
511      * @param item the item index (zero-based).
512      * @param crosshairState crosshair information for the plot
513      * (<code>null</code> permitted).
514      * @param pass the pass index.
515      */

516     public void drawItem(Graphics2D JavaDoc g2,
517                          XYItemRendererState state,
518                          Rectangle2D JavaDoc dataArea,
519                          PlotRenderingInfo info,
520                          XYPlot plot,
521                          ValueAxis domainAxis,
522                          ValueAxis rangeAxis,
523                          XYDataset dataset,
524                          int series,
525                          int item,
526                          CrosshairState crosshairState,
527                          int pass) {
528
529         boolean horiz;
530         PlotOrientation orientation = plot.getOrientation();
531         if (orientation == PlotOrientation.HORIZONTAL) {
532             horiz = true;
533         }
534         else if (orientation == PlotOrientation.VERTICAL) {
535             horiz = false;
536         }
537         else {
538             return;
539         }
540         
541         // setup for collecting optional entity info...
542
EntityCollection entities = null;
543         if (info != null) {
544             entities = info.getOwner().getEntityCollection();
545         }
546
547         OHLCDataset highLowData = (OHLCDataset) dataset;
548
549         double x = highLowData.getXValue(series, item);
550         double yHigh = highLowData.getHighValue(series, item);
551         double yLow = highLowData.getLowValue(series, item);
552         double yOpen = highLowData.getOpenValue(series, item);
553         double yClose = highLowData.getCloseValue(series, item);
554
555         RectangleEdge domainEdge = plot.getDomainAxisEdge();
556         double xx = domainAxis.valueToJava2D(x, dataArea, domainEdge);
557
558         RectangleEdge edge = plot.getRangeAxisEdge();
559         double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, edge);
560         double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, edge);
561         double yyOpen = rangeAxis.valueToJava2D(yOpen, dataArea, edge);
562         double yyClose = rangeAxis.valueToJava2D(yClose, dataArea, edge);
563
564         double volumeWidth;
565         double stickWidth;
566         if (this.candleWidth > 0) {
567             // These are deliberately not bounded to minimums/maxCandleWidth to
568
// retain old behaviour.
569
volumeWidth = this.candleWidth;
570             stickWidth = this.candleWidth;
571         }
572         else {
573             double xxWidth = 0;
574             int itemCount;
575             switch (this.autoWidthMethod) {
576             
577                 case WIDTHMETHOD_AVERAGE:
578                     itemCount = highLowData.getItemCount(series);
579                     if (horiz) {
580                         xxWidth = dataArea.getHeight() / itemCount;
581                     }
582                     else {
583                         xxWidth = dataArea.getWidth() / itemCount;
584                     }
585                     break;
586             
587                 case WIDTHMETHOD_SMALLEST:
588                     // Note: It would be nice to pre-calculate this per series
589
itemCount = highLowData.getItemCount(series);
590                     double lastPos = -1;
591                     xxWidth = dataArea.getWidth();
592                     for (int i = 0; i < itemCount; i++) {
593                         double pos = domainAxis.valueToJava2D(
594                             highLowData.getXValue(series, i), dataArea,
595                             domainEdge
596                         );
597                         if (lastPos != -1) {
598                             xxWidth = Math.min(
599                                 xxWidth, Math.abs(pos - lastPos)
600                             );
601                         }
602                         lastPos = pos;
603                     }
604                     break;
605             
606                 case WIDTHMETHOD_INTERVALDATA:
607                     IntervalXYDataset intervalXYData
608                         = (IntervalXYDataset) dataset;
609                     double startPos = domainAxis.valueToJava2D(
610                         intervalXYData.getStartXValue(series, item), dataArea,
611                         plot.getDomainAxisEdge()
612                     );
613                     double endPos = domainAxis.valueToJava2D(
614                         intervalXYData.getEndXValue(series, item), dataArea,
615                         plot.getDomainAxisEdge()
616                     );
617                     xxWidth = Math.abs(endPos - startPos);
618                     break;
619                 
620             }
621             xxWidth -= 2 * this.autoWidthGap;
622             xxWidth *= this.autoWidthFactor;
623             xxWidth = Math.min(xxWidth, this.maxCandleWidth);
624             volumeWidth = Math.max(Math.min(1, this.maxCandleWidth), xxWidth);
625             stickWidth = Math.max(Math.min(3, this.maxCandleWidth), xxWidth);
626         }
627
628         Paint JavaDoc p = getItemPaint(series, item);
629         Stroke JavaDoc s = getItemStroke(series, item);
630
631         g2.setStroke(s);
632
633         if (this.drawVolume) {
634             int volume = (int) highLowData.getVolumeValue(series, item);
635             double volumeHeight = volume / this.maxVolume;
636
637             double min, max;
638             if (horiz) {
639                 min = dataArea.getMinX();
640                 max = dataArea.getMaxX();
641             }
642             else {
643                 min = dataArea.getMinY();
644                 max = dataArea.getMaxY();
645             }
646
647             double zzVolume = volumeHeight * (max - min);
648
649             g2.setPaint(Color.gray);
650             Composite JavaDoc originalComposite = g2.getComposite();
651             g2.setComposite(
652                 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f)
653             );
654
655             if (horiz) {
656                 g2.fill(new Rectangle2D.Double JavaDoc(min,
657                                                xx - volumeWidth / 2,
658                                                zzVolume, volumeWidth));
659             }
660             else {
661                 g2.fill(
662                     new Rectangle2D.Double JavaDoc(
663                         xx - volumeWidth / 2,
664                         max - zzVolume, volumeWidth, zzVolume
665                     )
666                 );
667             }
668
669             g2.setComposite(originalComposite);
670         }
671
672         g2.setPaint(p);
673
674         double yyMaxOpenClose = Math.max(yyOpen, yyClose);
675         double yyMinOpenClose = Math.min(yyOpen, yyClose);
676         double maxOpenClose = Math.max(yOpen, yClose);
677         double minOpenClose = Math.min(yOpen, yClose);
678
679         // draw the upper shadow
680
if (yHigh > maxOpenClose) {
681             if (horiz) {
682                 g2.draw(new Line2D.Double JavaDoc(yyHigh, xx, yyMaxOpenClose, xx));
683             }
684             else {
685                 g2.draw(new Line2D.Double JavaDoc(xx, yyHigh, xx, yyMaxOpenClose));
686             }
687         }
688
689         // draw the lower shadow
690
if (yLow < minOpenClose) {
691             if (horiz) {
692                 g2.draw(new Line2D.Double JavaDoc(yyLow, xx, yyMinOpenClose, xx));
693             }
694             else {
695                 g2.draw(new Line2D.Double JavaDoc(xx, yyLow, xx, yyMinOpenClose));
696             }
697         }
698
699         // draw the body
700
Shape JavaDoc body = null;
701         if (horiz) {
702             body = new Rectangle2D.Double JavaDoc(
703                 yyMinOpenClose, xx - stickWidth / 2,
704                 yyMaxOpenClose - yyMinOpenClose, stickWidth
705             );
706         }
707         else {
708             body = new Rectangle2D.Double JavaDoc(
709                 xx - stickWidth / 2, yyMinOpenClose,
710                 stickWidth, yyMaxOpenClose - yyMinOpenClose
711             );
712         }
713         if (yClose > yOpen) {
714             if (this.upPaint != null) {
715                 g2.setPaint(this.upPaint);
716                 g2.fill(body);
717             }
718         }
719         else {
720             if (this.downPaint != null) {
721                 g2.setPaint(this.downPaint);
722             }
723             g2.fill(body);
724         }
725         g2.setPaint(p);
726         g2.draw(body);
727
728         // add an entity for the item...
729
if (entities != null) {
730             String JavaDoc tip = null;
731             XYToolTipGenerator generator = getToolTipGenerator(series, item);
732             if (generator != null) {
733                 tip = generator.generateToolTip(dataset, series, item);
734             }
735             String JavaDoc url = null;
736             if (getURLGenerator() != null) {
737                 url = getURLGenerator().generateURL(dataset, series, item);
738             }
739             XYItemEntity entity = new XYItemEntity(body, dataset, series, item,
740                     tip, url);
741             entities.add(entity);
742         }
743
744     }
745
746     /**
747      * Tests this renderer for equality with another object.
748      *
749      * @param obj the object (<code>null</code> permitted).
750      *
751      * @return <code>true</code> or <code>false</code>.
752      */

753     public boolean equals(Object JavaDoc obj) {
754         if (obj == this) {
755             return true;
756         }
757         if (! (obj instanceof CandlestickRenderer)) {
758             return false;
759         }
760         CandlestickRenderer that = (CandlestickRenderer) obj;
761         if (this.candleWidth != that.candleWidth) {
762             return false;
763         }
764         if (!PaintUtilities.equal(this.upPaint, that.upPaint)) {
765             return false;
766         }
767         if (!PaintUtilities.equal(this.downPaint, that.downPaint)) {
768             return false;
769         }
770         if (this.drawVolume != that.drawVolume) {
771             return false;
772         }
773         if (this.maxCandleWidthInMilliseconds
774                 != that.maxCandleWidthInMilliseconds) {
775             return false;
776         }
777         if (this.autoWidthMethod != that.autoWidthMethod) {
778             return false;
779         }
780         if (this.autoWidthFactor != that.autoWidthFactor) {
781             return false;
782         }
783         if (this.autoWidthGap != that.autoWidthGap) {
784             return false;
785         }
786         return super.equals(obj);
787     }
788
789     /**
790      * Returns a clone of the renderer.
791      *
792      * @return A clone.
793      *
794      * @throws CloneNotSupportedException if the renderer cannot be cloned.
795      */

796     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
797         return super.clone();
798     }
799
800     /**
801      * Provides serialization support.
802      *
803      * @param stream the output stream.
804      *
805      * @throws IOException if there is an I/O error.
806      */

807     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
808         stream.defaultWriteObject();
809         SerialUtilities.writePaint(this.upPaint, stream);
810         SerialUtilities.writePaint(this.downPaint, stream);
811     }
812
813     /**
814      * Provides serialization support.
815      *
816      * @param stream the input stream.
817      *
818      * @throws IOException if there is an I/O error.
819      * @throws ClassNotFoundException if there is a classpath problem.
820      */

821     private void readObject(ObjectInputStream JavaDoc stream)
822             throws IOException JavaDoc, ClassNotFoundException JavaDoc {
823         stream.defaultReadObject();
824         this.upPaint = SerialUtilities.readPaint(stream);
825         this.downPaint = SerialUtilities.readPaint(stream);
826     }
827     
828 }
829
Popular Tags