KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ======================================
2  * JFreeChart : a free Java chart library
3  * ======================================
4  *
5  * Project Info: http://www.jfree.org/jfreechart/index.html
6  * Project Lead: David Gilbert (david.gilbert@object-refinery.com);
7  *
8  * (C) Copyright 2000-2003, by Object Refinery Limited and Contributors.
9  *
10  * This library is free software; you can redistribute it and/or modify it under the terms
11  * of the GNU Lesser General Public License as published by the Free Software Foundation;
12  * either version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License along with this
19  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * ------------------------
23  * CandlestickRenderer.java
24  * ------------------------
25  * (C) Copyright 2001-2003, by Object Refinery Limited.
26  *
27  * Original Authors: David Gilbert (for Object Refinery Limited);
28  * Sylvain Vieujot;
29  * Contributor(s): Richard Atkinson;
30  * Christian W. Zuckschwerdt;
31  * Jerome Fisher;
32  *
33  * $Id: CandlestickRenderer.java,v 1.23 2003/11/03 14:21:27 mungady Exp $
34  *
35  * Changes
36  * -------
37  * 13-Dec-2001 : Version 1. Based on code in the CandlestickPlot class, written by Sylvain
38  * Vieujot, which now is redundant (DG);
39  * 23-Jan-2002 : Added DrawInfo parameter to drawItem(...) method (DG);
40  * 28-Mar-2002 : Added a property change listener mechanism so that renderers no longer need to be
41  * immutable. Added properties for up and down colors (DG);
42  * 04-Apr-2002 : Updated with new automatic width calculation and optional volume display,
43  * contributed by Sylvain Vieujot (DG);
44  * 09-Apr-2002 : Removed translatedRangeZero from the drawItem(...) method, and changed the return
45  * type of the drawItem method to void, reflecting a change in the XYItemRenderer
46  * interface. Added tooltip code to drawItem(...) method (DG);
47  * 25-Jun-2002 : Removed redundant code (DG);
48  * 05-Aug-2002 : Small modification to drawItem method to support URLs for HTML image maps (RA);
49  * 19-Sep-2002 : Fixed errors reported by Checkstyle (DG);
50  * 25-Mar-2003 : Implemented Serializable (DG);
51  * 01-May-2003 : Modified drawItem(...) method signature (DG);
52  * 30-Jun-2003 : Added support for PlotOrientation (for completeness, this renderer is unlikely
53  * to be used with a HORIZONTAL orientation) (DG);
54  * 30-Jul-2003 : Modified entity constructor (CZ);
55  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
56  * 29-Aug-2003 : Moved maxVolume calculation to initialise method (see bug report 796619) (DG);
57  * 02-Sep-2003 : Added maxCandleWidthInMilliseconds as workaround for bug 796621 (DG);
58  * 08-Sep-2003 : Changed ValueAxis API (DG);
59  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
60  * 13-Oct-2003 : Applied patch from Jerome Fisher to improve auto width calculations (DG);
61  *
62  */

63
64 package org.jfree.chart.renderer;
65
66 import java.awt.AlphaComposite JavaDoc;
67 import java.awt.Color JavaDoc;
68 import java.awt.Composite JavaDoc;
69 import java.awt.Graphics2D JavaDoc;
70 import java.awt.Paint JavaDoc;
71 import java.awt.Shape JavaDoc;
72 import java.awt.Stroke JavaDoc;
73 import java.awt.geom.Line2D JavaDoc;
74 import java.awt.geom.Rectangle2D JavaDoc;
75 import java.io.IOException JavaDoc;
76 import java.io.ObjectInputStream JavaDoc;
77 import java.io.ObjectOutputStream JavaDoc;
78 import java.io.Serializable JavaDoc;
79
80 import org.jfree.chart.CrosshairInfo;
81 import org.jfree.chart.axis.ValueAxis;
82 import org.jfree.chart.entity.EntityCollection;
83 import org.jfree.chart.entity.XYItemEntity;
84 import org.jfree.chart.labels.HighLowToolTipGenerator;
85 import org.jfree.chart.labels.XYToolTipGenerator;
86 import org.jfree.chart.plot.PlotOrientation;
87 import org.jfree.chart.plot.PlotRenderingInfo;
88 import org.jfree.chart.plot.XYPlot;
89 import org.jfree.data.HighLowDataset;
90 import org.jfree.data.XYDataset;
91 import org.jfree.data.IntervalXYDataset;
92 import org.jfree.io.SerialUtilities;
93 import org.jfree.ui.RectangleEdge;
94 import org.jfree.util.PublicCloneable;
95
96 /**
97  * A renderer that draws candlesticks on an {@link XYPlot} (requires a {@link HighLowDataset}).
98  *
99  * @author Sylvain Vieujot
100  */

101 public class CandlestickRenderer extends AbstractXYItemRenderer
102                                  implements XYItemRenderer,
103                                             Cloneable JavaDoc,
104                                             PublicCloneable,
105                                             Serializable JavaDoc {
106                                      
107     /** The average width method. */
108     public static final int WIDTHMETHOD_AVERAGE = 0;
109     
110     /** The smallest width method. */
111     public static final int WIDTHMETHOD_SMALLEST = 1;
112     
113     /** The interval data method. */
114     public static final int WIDTHMETHOD_INTERVALDATA = 2;
115
116     /** The method of automatically calculating the candle width. */
117     private int autoWidthMethod = WIDTHMETHOD_AVERAGE;
118
119     /**
120      * The number (generally between 0.0 and 1.0) by which the available space automatically
121      * calculated for the candles will be multiplied to determine the actual width to use.
122      */

123     private double autoWidthFactor = 4.5 / 7;
124
125     /** The minimum gap between one candle and the next */
126     private double autoWidthGap = 0.0;
127
128     /** The candle width. */
129     private double candleWidth;
130     
131     /** The maximum candlewidth in milliseconds. */
132     private double maxCandleWidthInMilliseconds = 1000.0 * 60.0 * 60.0 * 20.0;
133     
134     /** Temporary storage for the maximum candle width. */
135     private double maxCandleWidth;
136
137     /** The paint used to fill the candle when the price moved up from open to close. */
138     private transient Paint JavaDoc upPaint;
139
140     /** The paint used to fill the candle when the price moved down from open to close. */
141     private transient Paint JavaDoc downPaint;
142
143     /** A flag controlling whether or not volume bars are drawn on the chart. */
144     private boolean drawVolume;
145     
146     /** Temporary storage for the maximum volume. */
147     private transient double maxVolume;
148
149     /**
150      * Creates a new renderer for candlestick charts.
151      */

152     public CandlestickRenderer() {
153         this(-1.0);
154     }
155
156     /**
157      * Creates a new renderer for candlestick charts.
158      * <P>
159      * Use -1 for the candle width if you prefer the width to be calculated automatically.
160      *
161      * @param candleWidth The candle width.
162      */

163     public CandlestickRenderer(double candleWidth) {
164         this(candleWidth, true, new HighLowToolTipGenerator());
165     }
166
167     /**
168      * Creates a new renderer for candlestick charts.
169      * <P>
170      * Use -1 for the candle width if you prefer the width to be calculated automatically.
171      *
172      * @param candleWidth The candle width.
173      * @param drawVolume A flag indicating whether or not volume bars should be drawn.
174      * @param toolTipGenerator The tool tip generator. <code>null</code> is none.
175      */

176     public CandlestickRenderer(double candleWidth, boolean drawVolume,
177                                XYToolTipGenerator toolTipGenerator) {
178
179         super();
180         setToolTipGenerator(toolTipGenerator);
181         this.candleWidth = candleWidth;
182         this.drawVolume = drawVolume;
183         this.upPaint = Color.green;
184         this.downPaint = Color.red;
185
186     }
187
188     /**
189      * Returns the width of each candle.
190      *
191      * @return The candle width.
192      * @see org.jfree.chart.renderer.CandlestickRenderer#setCandleWidth
193      */

194     public double getCandleWidth() {
195         return this.candleWidth;
196     }
197
198     /**
199      * Sets the candle width.
200      * <P>
201      * If you set the width to a negative value, the renderer will calculate
202      * the candle width automatically based on the space available on the chart.
203      *
204      * @param width The width.
205      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthMethod
206      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthGap
207      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthFactor
208      * @see org.jfree.chart.renderer.CandlestickRenderer#setMaxCandleWidthInMilliseconds
209      */

210     public void setCandleWidth(double width) {
211
212         if (width != this.candleWidth) {
213             Double JavaDoc old = new Double JavaDoc(this.candleWidth);
214             this.candleWidth = width;
215             firePropertyChanged("CandleStickRenderer.candleWidth", old, new Double JavaDoc(width));
216         }
217
218     }
219
220     /**
221      * Returns the maximum width (in milliseconds) of each candle.
222      *
223      * @return The maximum candle width in milliseconds.
224      */

225     public double getMaxCandleWidthInMilliseconds() {
226         return this.maxCandleWidthInMilliseconds;
227     }
228
229     /**
230      * Sets the maximum candle width (in milliseconds).
231      *
232      * @param millis The maximum width.
233      * @see org.jfree.chart.renderer.CandlestickRenderer#setCandleWidth
234      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthMethod
235      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthGap
236      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthFactor
237      */

238     public void setMaxCandleWidthInMilliseconds(double millis) {
239
240         this.maxCandleWidthInMilliseconds = millis;
241         firePropertyChanged("CandlestickRenderer.maxCandleWidthInMilliseconds",
242                             null, new Double JavaDoc(millis));
243
244     }
245
246     /**
247      * Returns the method of automatically calculating the candle width.
248      *
249      * @return The method of automatically calculating the candle width.
250      */

251     public int getAutoWidthMethod() {
252         return autoWidthMethod;
253     }
254
255     /**
256      * Sets the method of automatically calculating the candle width.
257      * <p>
258      * <code>WIDTHMETHOD_AVERAGE</code>: Divides the entire display (ignoring scale factor) by the
259      * number of items, and uses this as the available width.<br>
260      * <code>WIDTHMETHOD_SMALLEST</code>: Checks the interval between each item, and uses the
261      * smallest as the available width.<br>
262      * <code>WIDTHMETHOD_INTERVALDATA</code>: Assumes that the dataset supports the
263      * IntervalXYDataset interface, and uses the startXValue - endXValue as the available width.
264      * <br>
265      *
266      * @param autoWidthMethod The method of automatically calculating the candle width.
267      *
268      * @see org.jfree.chart.renderer.CandlestickRenderer#WIDTHMETHOD_AVERAGE
269      * @see org.jfree.chart.renderer.CandlestickRenderer#WIDTHMETHOD_SMALLEST
270      * @see org.jfree.chart.renderer.CandlestickRenderer#WIDTHMETHOD_INTERVALDATA
271      * @see org.jfree.chart.renderer.CandlestickRenderer#setCandleWidth
272      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthGap
273      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthFactor
274      * @see org.jfree.chart.renderer.CandlestickRenderer#setMaxCandleWidthInMilliseconds
275      */

276     public void setAutoWidthMethod(int autoWidthMethod) {
277         if (this.autoWidthMethod != autoWidthMethod) {
278             Integer JavaDoc oldValue = new Integer JavaDoc(this.autoWidthMethod);
279             this.autoWidthMethod = autoWidthMethod;
280             firePropertyChanged("CandlestickRenderer.autoWidthMethod",
281                                 oldValue, new Integer JavaDoc(autoWidthMethod));
282         }
283     }
284
285     /**
286      * Returns the factor by which the available space automatically calculated for the candles
287      * will be multiplied to determine the actual width to use.
288      *
289      * @return The width factor (generally between 0.0 and 1.0).
290      */

291     public double getAutoWidthFactor() {
292         return autoWidthFactor;
293     }
294
295     /**
296      * Sets the factor by which the available space automatically calculated for the candles will
297      * be multiplied to determine the actual width to use.
298      *
299      * @param autoWidthFactor The width factor (generally between 0.0 and 1.0).
300      * @see org.jfree.chart.renderer.CandlestickRenderer#setCandleWidth
301      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthMethod
302      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthGap
303      * @see org.jfree.chart.renderer.CandlestickRenderer#setMaxCandleWidthInMilliseconds
304      */

305     public void setAutoWidthFactor(double autoWidthFactor) {
306         if (this.autoWidthFactor != autoWidthFactor) {
307             Double JavaDoc oldValue = new Double JavaDoc(this.autoWidthFactor);
308             this.autoWidthFactor = autoWidthFactor;
309             firePropertyChanged("CandlestickRenderer.autoWidthFactor",
310                                 oldValue, new Double JavaDoc(autoWidthFactor));
311         }
312     }
313
314     /**
315      * Returns the amount of space to leave on the left and right of each candle when
316      * automatically calculating widths.
317      *
318      * @return The gap.
319      */

320     public double getAutoWidthGap() {
321         return autoWidthGap;
322     }
323
324     /**
325      * Sets the amount of space to leave on the left and right of each candle when automatically
326      * calculating widths.
327      *
328      * @param autoWidthGap The gap.
329      * @see org.jfree.chart.renderer.CandlestickRenderer#setCandleWidth
330      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthMethod
331      * @see org.jfree.chart.renderer.CandlestickRenderer#setAutoWidthFactor
332      * @see org.jfree.chart.renderer.CandlestickRenderer#setMaxCandleWidthInMilliseconds
333      */

334     public void setAutoWidthGap(double autoWidthGap) {
335         if (this.autoWidthGap != autoWidthGap) {
336             Double JavaDoc oldValue = new Double JavaDoc(this.autoWidthGap);
337             this.autoWidthGap = autoWidthGap;
338             firePropertyChanged("CandlestickRenderer.autoWidthGap",
339                                 oldValue, new Double JavaDoc(autoWidthGap));
340         }
341     }
342
343     /**
344      * Returns the paint used to fill candles when the price moves up from open
345      * to close.
346      *
347      * @return The paint.
348      */

349     public Paint JavaDoc getUpPaint() {
350         return this.upPaint;
351     }
352
353     /**
354      * Sets the paint used to fill candles when the price moves up from open
355      * to close.
356      * <P>
357      * Registered property change listeners are notified that the
358      * "CandleStickRenderer.upPaint" property has changed.
359      *
360      * @param paint The paint.
361      */

362     public void setUpPaint(Paint JavaDoc paint) {
363
364         Paint JavaDoc old = this.upPaint;
365         this.upPaint = paint;
366         firePropertyChanged("CandleStickRenderer.upPaint", old, paint);
367
368     }
369
370     /**
371      * Returns the paint used to fill candles when the price moves down from
372      * open to close.
373      *
374      * @return The paint.
375      */

376     public Paint JavaDoc getDownPaint() {
377         return this.downPaint;
378     }
379
380     /**
381      * Sets the paint used to fill candles when the price moves down from open
382      * to close.
383      * <P>
384      * Registered property change listeners are notified that the
385      * "CandleStickRenderer.downPaint" property has changed.
386      *
387      * @param paint The paint.
388      */

389     public void setDownPaint(Paint JavaDoc paint) {
390         Paint JavaDoc old = this.downPaint;
391         this.downPaint = paint;
392         firePropertyChanged("CandleStickRenderer.downPaint", old, paint);
393     }
394
395     /**
396      * Returns a flag indicating whether or not volume bars are drawn on the
397      * chart.
398      *
399      * @return <code>true</code> if volume bars are drawn on the chart.
400      */

401     public boolean drawVolume() {
402         return this.drawVolume;
403     }
404
405     /**
406      * Sets a flag that controls whether or not volume bars are drawn in the
407      * background.
408      *
409      * @param flag The flag.
410      */

411     public void setDrawVolume(boolean flag) {
412
413         if (this.drawVolume != flag) {
414             this.drawVolume = flag;
415             firePropertyChanged("CandlestickRenderer.drawVolume", null, new Boolean JavaDoc(flag));
416         }
417
418     }
419
420     /**
421      * Initialises the renderer then returns the number of 'passes' through the data that the
422      * renderer will require (usually just one). This method will be called before the first
423      * item is rendered, giving the renderer an opportunity to initialise any
424      * state information it wants to maintain. The renderer can do nothing if it chooses.
425      *
426      * @param g2 the graphics device.
427      * @param dataArea the area inside the axes.
428      * @param plot the plot.
429      * @param dataset the data.
430      * @param info an optional info collection object to return data back to the caller.
431      *
432      * @return The number of passes the renderer requires.
433      */

434     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
435                                           Rectangle2D JavaDoc dataArea,
436                                           XYPlot plot,
437                                           XYDataset dataset,
438                                           PlotRenderingInfo info) {
439           
440         // calculate the maximum allowed candle width from the axis...
441
ValueAxis axis = plot.getDomainAxis();
442         double x1 = axis.getLowerBound();
443         double x2 = x1 + this.maxCandleWidthInMilliseconds;
444         RectangleEdge edge = plot.getDomainAxisEdge();
445         double xx1 = axis.translateValueToJava2D(x1, dataArea, edge);
446         double xx2 = axis.translateValueToJava2D(x2, dataArea, edge);
447         this.maxCandleWidth = Math.abs(xx2 - xx1); // Absolute value, since the relative x
448
// positions are reversed for horizontal orientation
449

450         // calculate the highest volume in the dataset...
451
if (this.drawVolume) {
452             HighLowDataset highLowDataset = (HighLowDataset) dataset;
453             this.maxVolume = 0.0;
454             for (int series = 0; series < highLowDataset.getSeriesCount(); series++) {
455                 for (int item = 0; item < highLowDataset.getItemCount(series); item++) {
456                     double volume = highLowDataset.getVolumeValue(series, item).doubleValue();
457                     if (volume > this.maxVolume) {
458                         this.maxVolume = volume;
459                     }
460                     
461                 }
462             }
463         }
464         
465         return new XYItemRendererState(info);
466     }
467
468     /**
469      * Draws the visual representation of a single data item.
470      *
471      * @param g2 the graphics device.
472      * @param state the renderer state.
473      * @param dataArea the area within which the plot is being drawn.
474      * @param info collects info about the drawing.
475      * @param plot the plot (can be used to obtain standard color information etc).
476      * @param domainAxis the domain axis.
477      * @param rangeAxis the range axis.
478      * @param dataset the dataset.
479      * @param series the series index (zero-based).
480      * @param item the item index (zero-based).
481      * @param crosshairInfo information about crosshairs on a plot.
482      * @param pass the pass index.
483      */

484     public void drawItem(Graphics2D JavaDoc g2,
485                          XYItemRendererState state,
486                          Rectangle2D JavaDoc dataArea,
487                          PlotRenderingInfo info,
488                          XYPlot plot,
489                          ValueAxis domainAxis,
490                          ValueAxis rangeAxis,
491                          XYDataset dataset,
492                          int series,
493                          int item,
494                          CrosshairInfo crosshairInfo,
495                          int pass) {
496
497         boolean horiz;
498         PlotOrientation orientation = plot.getOrientation();
499         if (orientation == PlotOrientation.HORIZONTAL) {
500             horiz = true;
501         }
502         else if (orientation == PlotOrientation.VERTICAL) {
503             horiz = false;
504         }
505         else {
506             return;
507         }
508         
509         // setup for collecting optional entity info...
510
EntityCollection entities = null;
511         if (info != null) {
512             entities = info.getOwner().getEntityCollection();
513         }
514
515         HighLowDataset highLowData = (HighLowDataset) dataset;
516
517         Number JavaDoc x = highLowData.getXValue(series, item);
518         Number JavaDoc yHigh = highLowData.getHighValue(series, item);
519         Number JavaDoc yLow = highLowData.getLowValue(series, item);
520         Number JavaDoc yOpen = highLowData.getOpenValue(series, item);
521         Number JavaDoc yClose = highLowData.getCloseValue(series, item);
522
523         RectangleEdge domainEdge = plot.getDomainAxisEdge();
524         double xx = domainAxis.translateValueToJava2D(x.doubleValue(), dataArea, domainEdge);
525
526         RectangleEdge edge = plot.getRangeAxisEdge();
527         double yyHigh = rangeAxis.translateValueToJava2D(yHigh.doubleValue(), dataArea, edge);
528         double yyLow = rangeAxis.translateValueToJava2D(yLow.doubleValue(), dataArea, edge);
529         double yyOpen = rangeAxis.translateValueToJava2D(yOpen.doubleValue(), dataArea, edge);
530         double yyClose = rangeAxis.translateValueToJava2D(yClose.doubleValue(), dataArea, edge);
531
532         double volumeWidth;
533         double stickWidth;
534         if (candleWidth > 0) {
535             // These are deliberately not bounded to minimums/maxCandleWidth to retain old
536
// behaviour.
537
volumeWidth = candleWidth;
538             stickWidth = candleWidth;
539         }
540         else {
541             double xxWidth = 0;
542             int itemCount;
543             switch (autoWidthMethod) {
544             
545                 case WIDTHMETHOD_AVERAGE:
546                     itemCount = highLowData.getItemCount(series);
547                     if (horiz) {
548                         xxWidth = dataArea.getHeight() / itemCount;
549                     }
550                     else {
551                         xxWidth = dataArea.getWidth() / itemCount;
552                     }
553                     break;
554             
555                 case WIDTHMETHOD_SMALLEST:
556                     // Note: It would be nice to pre-calculate this per series
557
itemCount = highLowData.getItemCount(series);
558                     double lastPos = -1;
559                     xxWidth = dataArea.getWidth();
560                     for (int i = 0; i < itemCount; i++) {
561                         double pos = domainAxis.translateValueToJava2D(
562                             highLowData.getXValue(series, i).doubleValue(), dataArea, domainEdge);
563                         if (lastPos != -1) {
564                             xxWidth = Math.min(xxWidth, Math.abs(pos - lastPos));
565                         }
566                         lastPos = pos;
567                     }
568                     break;
569             
570                 case WIDTHMETHOD_INTERVALDATA:
571                     IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
572                     double startPos = domainAxis.translateValueToJava2D(
573                         intervalXYData.getStartXValue(series, item).doubleValue(), dataArea,
574                         plot.getDomainAxisEdge());
575                     double endPos = domainAxis.translateValueToJava2D(
576                         intervalXYData.getEndXValue(series, item).doubleValue(), dataArea,
577                         plot.getDomainAxisEdge());
578                     xxWidth = Math.abs(endPos - startPos);
579                     break;
580                 
581             }
582             xxWidth -= 2 * autoWidthGap;
583             xxWidth *= autoWidthFactor;
584             xxWidth = Math.min(xxWidth, maxCandleWidth);
585             volumeWidth = Math.max(Math.min(1, maxCandleWidth), xxWidth);
586             stickWidth = Math.max(Math.min(3, maxCandleWidth), xxWidth);
587         }
588
589         Paint JavaDoc p = getItemPaint(series, item);
590         Stroke JavaDoc s = getItemStroke(series, item);
591
592         g2.setStroke(s);
593
594         if (drawVolume) {
595             int volume = highLowData.getVolumeValue(series, item).intValue();
596             double volumeHeight = volume / this.maxVolume;
597
598             double min, max;
599             if (horiz) {
600                 min = dataArea.getMinX();
601                 max = dataArea.getMaxX();
602             }
603             else {
604                 min = dataArea.getMinY();
605                 max = dataArea.getMaxY();
606             }
607
608             double zzVolume = volumeHeight * (max - min);
609
610             g2.setPaint(Color.gray);
611             Composite JavaDoc originalComposite = g2.getComposite();
612             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
613
614             if (horiz) {
615                 g2.fill(new Rectangle2D.Double JavaDoc(min,
616                                                xx - volumeWidth / 2,
617                                                zzVolume, volumeWidth));
618             }
619             else {
620                 g2.fill(new Rectangle2D.Double JavaDoc(xx - volumeWidth / 2,
621                                                max - zzVolume, volumeWidth, zzVolume));
622             }
623
624             g2.setComposite(originalComposite);
625         }
626
627         g2.setPaint(p);
628
629         double yyMaxOpenClose = Math.max(yyOpen, yyClose);
630         double yyMinOpenClose = Math.min(yyOpen, yyClose);
631         double maxOpenClose = Math.max(yOpen.doubleValue(), yClose.doubleValue());
632         double minOpenClose = Math.min(yOpen.doubleValue(), yClose.doubleValue());
633
634         // draw the upper shadow
635
if (yHigh.doubleValue() > maxOpenClose) {
636             if (horiz) {
637                 g2.draw(new Line2D.Double JavaDoc(yyHigh, xx, yyMaxOpenClose, xx));
638             }
639             else {
640                 g2.draw(new Line2D.Double JavaDoc(xx, yyHigh, xx, yyMaxOpenClose));
641             }
642         }
643
644         // draw the lower shadow
645
if (yLow.doubleValue() < minOpenClose) {
646             if (horiz) {
647                 g2.draw(new Line2D.Double JavaDoc(yyLow, xx, yyMinOpenClose, xx));
648             }
649             else {
650                 g2.draw(new Line2D.Double JavaDoc(xx, yyLow, xx, yyMinOpenClose));
651             }
652         }
653
654         // draw the body
655
Shape JavaDoc body = null;
656         if (horiz) {
657             body = new Rectangle2D.Double JavaDoc(yyMinOpenClose, xx - stickWidth / 2,
658                                           yyMaxOpenClose - yyMinOpenClose, stickWidth);
659         }
660         else {
661             body = new Rectangle2D.Double JavaDoc(xx - stickWidth / 2, yyMinOpenClose,
662                                           stickWidth, yyMaxOpenClose - yyMinOpenClose);
663         }
664         if (yOpen.doubleValue() > yClose.doubleValue()) {
665             if (upPaint != null) {
666                 g2.setPaint(upPaint);
667                 g2.fill(body);
668             }
669         }
670         else {
671             if (downPaint != null) {
672                 g2.setPaint(downPaint);
673             }
674             g2.fill(body);
675         }
676         g2.setPaint(p);
677         g2.draw(body);
678
679         // add an entity for the item...
680
if (entities != null) {
681             String JavaDoc tip = null;
682             if (getToolTipGenerator() != null) {
683                 tip = getToolTipGenerator().generateToolTip(dataset, series, item);
684             }
685             String JavaDoc url = null;
686             if (getURLGenerator() != null) {
687                 url = getURLGenerator().generateURL(dataset, series, item);
688             }
689             XYItemEntity entity = new XYItemEntity(body, dataset, series, item, tip, url);
690             entities.addEntity(entity);
691         }
692
693     }
694
695     /**
696      * Tests this renderer for equality with another object.
697      *
698      * @param obj the object.
699      *
700      * @return <code>true</code> or <code>false</code>.
701      */

702     public boolean equals(Object JavaDoc obj) {
703
704         if (obj == null) {
705             return false;
706         }
707
708         if (obj == this) {
709             return true;
710         }
711
712         if (obj instanceof CandlestickRenderer) {
713             CandlestickRenderer renderer = (CandlestickRenderer) obj;
714             boolean result = super.equals(obj);
715             result = result && (this.candleWidth == renderer.getCandleWidth());
716             result = result && (this.upPaint.equals(renderer.getUpPaint()));
717             result = result && (this.downPaint.equals(renderer.getDownPaint()));
718             result = result && (this.drawVolume == renderer.drawVolume);
719             return result;
720         }
721
722         return false;
723
724     }
725
726     /**
727      * Returns a clone of the renderer.
728      *
729      * @return A clone.
730      *
731      * @throws CloneNotSupportedException if the renderer cannot be cloned.
732      */

733     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
734         return super.clone();
735     }
736
737     /**
738      * Provides serialization support.
739      *
740      * @param stream the output stream.
741      *
742      * @throws IOException if there is an I/O error.
743      */

744     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
745         stream.defaultWriteObject();
746         SerialUtilities.writePaint(this.upPaint, stream);
747         SerialUtilities.writePaint(this.downPaint, stream);
748     }
749
750     /**
751      * Provides serialization support.
752      *
753      * @param stream the input stream.
754      *
755      * @throws IOException if there is an I/O error.
756      * @throws ClassNotFoundException if there is a classpath problem.
757      */

758     private void readObject(ObjectInputStream JavaDoc stream) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
759         stream.defaultReadObject();
760         this.upPaint = SerialUtilities.readPaint(stream);
761         this.downPaint = SerialUtilities.readPaint(stream);
762     }
763     
764 }
765
Popular Tags