KickJava   Java API By Example, From Geeks To Geeks.

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


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  * XYLineAndShapeRenderer.java
29  * ---------------------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: XYLineAndShapeRenderer.java,v 1.20.2.7 2006/07/20 16:21:58 mungady Exp $
36  *
37  * Changes:
38  * --------
39  * 27-Jan-2004 : Version 1 (DG);
40  * 10-Feb-2004 : Minor change to drawItem() method to make cut-and-paste
41  * overriding easier (DG);
42  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
43  * 25-Aug-2004 : Added support for chart entities (required for tooltips) (DG);
44  * 24-Sep-2004 : Added flag to allow whole series to be drawn as a path
45  * (necessary when using a dashed stroke with many data
46  * items) (DG);
47  * 04-Oct-2004 : Renamed BooleanUtils --> BooleanUtilities (DG);
48  * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
49  * 27-Jan-2005 : The getLegendItem() method now omits hidden series (DG);
50  * 28-Jan-2005 : Added new constructor (DG);
51  * 09-Mar-2005 : Added fillPaint settings (DG);
52  * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
53  * 22-Jul-2005 : Renamed defaultLinesVisible --> baseLinesVisible,
54  * defaultShapesVisible --> baseShapesVisible and
55  * defaultShapesFilled --> baseShapesFilled (DG);
56  * 29-Jul-2005 : Added code to draw item labels (DG);
57  * ------------- JFREECHART 1.0.0 ---------------------------------------------
58  * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
59  *
60  */

61
62 package org.jfree.chart.renderer.xy;
63
64 import java.awt.Graphics2D JavaDoc;
65 import java.awt.Paint JavaDoc;
66 import java.awt.Shape JavaDoc;
67 import java.awt.Stroke JavaDoc;
68 import java.awt.geom.GeneralPath JavaDoc;
69 import java.awt.geom.Line2D JavaDoc;
70 import java.awt.geom.Rectangle2D JavaDoc;
71 import java.io.IOException JavaDoc;
72 import java.io.ObjectInputStream JavaDoc;
73 import java.io.ObjectOutputStream JavaDoc;
74 import java.io.Serializable JavaDoc;
75
76 import org.jfree.chart.LegendItem;
77 import org.jfree.chart.axis.ValueAxis;
78 import org.jfree.chart.entity.EntityCollection;
79 import org.jfree.chart.event.RendererChangeEvent;
80 import org.jfree.chart.plot.CrosshairState;
81 import org.jfree.chart.plot.PlotOrientation;
82 import org.jfree.chart.plot.PlotRenderingInfo;
83 import org.jfree.chart.plot.XYPlot;
84 import org.jfree.data.xy.XYDataset;
85 import org.jfree.io.SerialUtilities;
86 import org.jfree.ui.RectangleEdge;
87 import org.jfree.util.BooleanList;
88 import org.jfree.util.BooleanUtilities;
89 import org.jfree.util.ObjectUtilities;
90 import org.jfree.util.PublicCloneable;
91 import org.jfree.util.ShapeUtilities;
92
93 /**
94  * A renderer that can be used with the {@link XYPlot} class.
95  */

96 public class XYLineAndShapeRenderer extends AbstractXYItemRenderer
97                                     implements XYItemRenderer,
98                                                Cloneable JavaDoc,
99                                                PublicCloneable,
100                                                Serializable JavaDoc {
101
102     /** For serialization. */
103     private static final long serialVersionUID = -7435246895986425885L;
104     
105     /** A flag that controls whether or not lines are visible for ALL series. */
106     private Boolean JavaDoc linesVisible;
107
108     /**
109      * A table of flags that control (per series) whether or not lines are
110      * visible.
111      */

112     private BooleanList seriesLinesVisible;
113
114     /** The default value returned by the getLinesVisible() method. */
115     private boolean baseLinesVisible;
116
117     /** The shape that is used to represent a line in the legend. */
118     private transient Shape JavaDoc legendLine;
119     
120     /**
121      * A flag that controls whether or not shapes are visible for ALL series.
122      */

123     private Boolean JavaDoc shapesVisible;
124
125     /**
126      * A table of flags that control (per series) whether or not shapes are
127      * visible.
128      */

129     private BooleanList seriesShapesVisible;
130
131     /** The default value returned by the getShapeVisible() method. */
132     private boolean baseShapesVisible;
133
134     /** A flag that controls whether or not shapes are filled for ALL series. */
135     private Boolean JavaDoc shapesFilled;
136
137     /**
138      * A table of flags that control (per series) whether or not shapes are
139      * filled.
140      */

141     private BooleanList seriesShapesFilled;
142
143     /** The default value returned by the getShapeFilled() method. */
144     private boolean baseShapesFilled;
145     
146     /** A flag that controls whether outlines are drawn for shapes. */
147     private boolean drawOutlines;
148     
149     /**
150      * A flag that controls whether the fill paint is used for filling
151      * shapes.
152      */

153     private boolean useFillPaint;
154     
155     /**
156      * A flag that controls whether the outline paint is used for drawing shape
157      * outlines.
158      */

159     private boolean useOutlinePaint;
160     
161     /**
162      * A flag that controls whether or not each series is drawn as a single
163      * path.
164      */

165     private boolean drawSeriesLineAsPath;
166
167     /**
168      * Creates a new renderer with both lines and shapes visible.
169      */

170     public XYLineAndShapeRenderer() {
171         this(true, true);
172     }
173     
174     /**
175      * Creates a new renderer.
176      *
177      * @param lines lines visible?
178      * @param shapes shapes visible?
179      */

180     public XYLineAndShapeRenderer(boolean lines, boolean shapes) {
181         this.linesVisible = null;
182         this.seriesLinesVisible = new BooleanList();
183         this.baseLinesVisible = lines;
184         this.legendLine = new Line2D.Double JavaDoc(-7.0, 0.0, 7.0, 0.0);
185         
186         this.shapesVisible = null;
187         this.seriesShapesVisible = new BooleanList();
188         this.baseShapesVisible = shapes;
189         
190         this.shapesFilled = null;
191         this.useFillPaint = false; // use item paint for fills by default
192
this.seriesShapesFilled = new BooleanList();
193         this.baseShapesFilled = true;
194
195         this.drawOutlines = true;
196         this.useOutlinePaint = false; // use item paint for outlines by
197
// default, not outline paint
198

199         this.drawSeriesLineAsPath = false;
200     }
201     
202     /**
203      * Returns a flag that controls whether or not each series is drawn as a
204      * single path.
205      *
206      * @return A boolean.
207      *
208      * @see #setDrawSeriesLineAsPath(boolean)
209      */

210     public boolean getDrawSeriesLineAsPath() {
211         return this.drawSeriesLineAsPath;
212     }
213     
214     /**
215      * Sets the flag that controls whether or not each series is drawn as a
216      * single path.
217      *
218      * @param flag the flag.
219      *
220      * @see #getDrawSeriesLineAsPath()
221      */

222     public void setDrawSeriesLineAsPath(boolean flag) {
223         if (this.drawSeriesLineAsPath != flag) {
224             this.drawSeriesLineAsPath = flag;
225             notifyListeners(new RendererChangeEvent(this));
226         }
227     }
228     
229     /**
230      * Returns the number of passes through the data that the renderer requires
231      * in order to draw the chart. Most charts will require a single pass, but
232      * some require two passes.
233      *
234      * @return The pass count.
235      */

236     public int getPassCount() {
237         return 2;
238     }
239     
240     // LINES VISIBLE
241

242     /**
243      * Returns the flag used to control whether or not the shape for an item is
244      * visible.
245      *
246      * @param series the series index (zero-based).
247      * @param item the item index (zero-based).
248      *
249      * @return A boolean.
250      */

251     public boolean getItemLineVisible(int series, int item) {
252         Boolean JavaDoc flag = this.linesVisible;
253         if (flag == null) {
254             flag = getSeriesLinesVisible(series);
255         }
256         if (flag != null) {
257             return flag.booleanValue();
258         }
259         else {
260             return this.baseLinesVisible;
261         }
262     }
263
264     /**
265      * Returns a flag that controls whether or not lines are drawn for ALL
266      * series. If this flag is <code>null</code>, then the "per series"
267      * settings will apply.
268      *
269      * @return A flag (possibly <code>null</code>).
270      */

271     public Boolean JavaDoc getLinesVisible() {
272         return this.linesVisible;
273     }
274     
275     /**
276      * Sets a flag that controls whether or not lines are drawn between the
277      * items in ALL series, and sends a {@link RendererChangeEvent} to all
278      * registered listeners. You need to set this to <code>null</code> if you
279      * want the "per series" settings to apply.
280      *
281      * @param visible the flag (<code>null</code> permitted).
282      */

283     public void setLinesVisible(Boolean JavaDoc visible) {
284         this.linesVisible = visible;
285         notifyListeners(new RendererChangeEvent(this));
286     }
287
288     /**
289      * Sets a flag that controls whether or not lines are drawn between the
290      * items in ALL series, and sends a {@link RendererChangeEvent} to all
291      * registered listeners.
292      *
293      * @param visible the flag.
294      */

295     public void setLinesVisible(boolean visible) {
296         setLinesVisible(BooleanUtilities.valueOf(visible));
297     }
298
299     /**
300      * Returns the flag used to control whether or not the lines for a series
301      * are visible.
302      *
303      * @param series the series index (zero-based).
304      *
305      * @return The flag (possibly <code>null</code>).
306      */

307     public Boolean JavaDoc getSeriesLinesVisible(int series) {
308         return this.seriesLinesVisible.getBoolean(series);
309     }
310
311     /**
312      * Sets the 'lines visible' flag for a series.
313      *
314      * @param series the series index (zero-based).
315      * @param flag the flag (<code>null</code> permitted).
316      */

317     public void setSeriesLinesVisible(int series, Boolean JavaDoc flag) {
318         this.seriesLinesVisible.setBoolean(series, flag);
319         notifyListeners(new RendererChangeEvent(this));
320     }
321
322     /**
323      * Sets the 'lines visible' flag for a series.
324      *
325      * @param series the series index (zero-based).
326      * @param visible the flag.
327      */

328     public void setSeriesLinesVisible(int series, boolean visible) {
329         setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
330     }
331     
332     /**
333      * Returns the base 'lines visible' attribute.
334      *
335      * @return The base flag.
336      */

337     public boolean getBaseLinesVisible() {
338         return this.baseLinesVisible;
339     }
340
341     /**
342      * Sets the base 'lines visible' flag.
343      *
344      * @param flag the flag.
345      */

346     public void setBaseLinesVisible(boolean flag) {
347         this.baseLinesVisible = flag;
348         notifyListeners(new RendererChangeEvent(this));
349     }
350
351     /**
352      * Returns the shape used to represent a line in the legend.
353      *
354      * @return The legend line (never <code>null</code>).
355      */

356     public Shape JavaDoc getLegendLine() {
357         return this.legendLine;
358     }
359     
360     /**
361      * Sets the shape used as a line in each legend item and sends a
362      * {@link RendererChangeEvent} to all registered listeners.
363      *
364      * @param line the line (<code>null</code> not permitted).
365      */

366     public void setLegendLine(Shape JavaDoc line) {
367         if (line == null) {
368             throw new IllegalArgumentException JavaDoc("Null 'line' argument.");
369         }
370         this.legendLine = line;
371         notifyListeners(new RendererChangeEvent(this));
372     }
373
374     // SHAPES VISIBLE
375

376     /**
377      * Returns the flag used to control whether or not the shape for an item is
378      * visible.
379      * <p>
380      * The default implementation passes control to the
381      * <code>getSeriesShapesVisible</code> method. You can override this method
382      * if you require different behaviour.
383      *
384      * @param series the series index (zero-based).
385      * @param item the item index (zero-based).
386      *
387      * @return A boolean.
388      */

389     public boolean getItemShapeVisible(int series, int item) {
390         Boolean JavaDoc flag = this.shapesVisible;
391         if (flag == null) {
392             flag = getSeriesShapesVisible(series);
393         }
394         if (flag != null) {
395             return flag.booleanValue();
396         }
397         else {
398             return this.baseShapesVisible;
399         }
400     }
401
402     /**
403      * Returns the flag that controls whether the shapes are visible for the
404      * items in ALL series.
405      *
406      * @return The flag (possibly <code>null</code>).
407      */

408     public Boolean JavaDoc getShapesVisible() {
409         return this.shapesVisible;
410     }
411     
412     /**
413      * Sets the 'shapes visible' for ALL series and sends a
414      * {@link RendererChangeEvent} to all registered listeners.
415      *
416      * @param visible the flag (<code>null</code> permitted).
417      */

418     public void setShapesVisible(Boolean JavaDoc visible) {
419         this.shapesVisible = visible;
420         notifyListeners(new RendererChangeEvent(this));
421     }
422
423     /**
424      * Sets the 'shapes visible' for ALL series and sends a
425      * {@link RendererChangeEvent} to all registered listeners.
426      *
427      * @param visible the flag.
428      */

429     public void setShapesVisible(boolean visible) {
430         setShapesVisible(BooleanUtilities.valueOf(visible));
431     }
432
433     /**
434      * Returns the flag used to control whether or not the shapes for a series
435      * are visible.
436      *
437      * @param series the series index (zero-based).
438      *
439      * @return A boolean.
440      */

441     public Boolean JavaDoc getSeriesShapesVisible(int series) {
442         return this.seriesShapesVisible.getBoolean(series);
443     }
444
445     /**
446      * Sets the 'shapes visible' flag for a series and sends a
447      * {@link RendererChangeEvent} to all registered listeners.
448      *
449      * @param series the series index (zero-based).
450      * @param visible the flag.
451      */

452     public void setSeriesShapesVisible(int series, boolean visible) {
453         setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
454     }
455     
456     /**
457      * Sets the 'shapes visible' flag for a series and sends a
458      * {@link RendererChangeEvent} to all registered listeners.
459      *
460      * @param series the series index (zero-based).
461      * @param flag the flag.
462      */

463     public void setSeriesShapesVisible(int series, Boolean JavaDoc flag) {
464         this.seriesShapesVisible.setBoolean(series, flag);
465         notifyListeners(new RendererChangeEvent(this));
466     }
467
468     /**
469      * Returns the base 'shape visible' attribute.
470      *
471      * @return The base flag.
472      */

473     public boolean getBaseShapesVisible() {
474         return this.baseShapesVisible;
475     }
476
477     /**
478      * Sets the base 'shapes visible' flag.
479      *
480      * @param flag the flag.
481      */

482     public void setBaseShapesVisible(boolean flag) {
483         this.baseShapesVisible = flag;
484         notifyListeners(new RendererChangeEvent(this));
485     }
486
487     // SHAPES FILLED
488

489     /**
490      * Returns the flag used to control whether or not the shape for an item
491      * is filled.
492      * <p>
493      * The default implementation passes control to the
494      * <code>getSeriesShapesFilled</code> method. You can override this method
495      * if you require different behaviour.
496      *
497      * @param series the series index (zero-based).
498      * @param item the item index (zero-based).
499      *
500      * @return A boolean.
501      */

502     public boolean getItemShapeFilled(int series, int item) {
503         Boolean JavaDoc flag = this.shapesFilled;
504         if (flag == null) {
505             flag = getSeriesShapesFilled(series);
506         }
507         if (flag != null) {
508             return flag.booleanValue();
509         }
510         else {
511             return this.baseShapesFilled;
512         }
513     }
514
515     /**
516      * Sets the 'shapes filled' for ALL series and sends a
517      * {@link RendererChangeEvent} to all registered listeners.
518      *
519      * @param filled the flag.
520      */

521     public void setShapesFilled(boolean filled) {
522         setShapesFilled(BooleanUtilities.valueOf(filled));
523     }
524
525     /**
526      * Sets the 'shapes filled' for ALL series and sends a
527      * {@link RendererChangeEvent} to all registered listeners.
528      *
529      * @param filled the flag (<code>null</code> permitted).
530      */

531     public void setShapesFilled(Boolean JavaDoc filled) {
532         this.shapesFilled = filled;
533         notifyListeners(new RendererChangeEvent(this));
534     }
535     
536     /**
537      * Returns the flag used to control whether or not the shapes for a series
538      * are filled.
539      *
540      * @param series the series index (zero-based).
541      *
542      * @return A boolean.
543      */

544     public Boolean JavaDoc getSeriesShapesFilled(int series) {
545         return this.seriesShapesFilled.getBoolean(series);
546     }
547
548     /**
549      * Sets the 'shapes filled' flag for a series.
550      *
551      * @param series the series index (zero-based).
552      * @param flag the flag.
553      */

554     public void setSeriesShapesFilled(int series, boolean flag) {
555         setSeriesShapesFilled(series, BooleanUtilities.valueOf(flag));
556     }
557
558     /**
559      * Sets the 'shapes filled' flag for a series.
560      *
561      * @param series the series index (zero-based).
562      * @param flag the flag.
563      */

564     public void setSeriesShapesFilled(int series, Boolean JavaDoc flag) {
565         this.seriesShapesFilled.setBoolean(series, flag);
566         notifyListeners(new RendererChangeEvent(this));
567     }
568
569     /**
570      * Returns the base 'shape filled' attribute.
571      *
572      * @return The base flag.
573      */

574     public boolean getBaseShapesFilled() {
575         return this.baseShapesFilled;
576     }
577
578     /**
579      * Sets the base 'shapes filled' flag.
580      *
581      * @param flag the flag.
582      */

583     public void setBaseShapesFilled(boolean flag) {
584         this.baseShapesFilled = flag;
585         notifyListeners(new RendererChangeEvent(this));
586     }
587
588     /**
589      * Returns <code>true</code> if outlines should be drawn for shapes, and
590      * <code>false</code> otherwise.
591      *
592      * @return A boolean.
593      */

594     public boolean getDrawOutlines() {
595         return this.drawOutlines;
596     }
597     
598     /**
599      * Sets the flag that controls whether outlines are drawn for
600      * shapes, and sends a {@link RendererChangeEvent} to all registered
601      * listeners.
602      * <P>
603      * In some cases, shapes look better if they do NOT have an outline, but
604      * this flag allows you to set your own preference.
605      *
606      * @param flag the flag.
607      */

608     public void setDrawOutlines(boolean flag) {
609         this.drawOutlines = flag;
610         notifyListeners(new RendererChangeEvent(this));
611     }
612     
613     /**
614      * Returns <code>true</code> if the renderer should use the fill paint
615      * setting to fill shapes, and <code>false</code> if it should just
616      * use the regular paint.
617      *
618      * @return A boolean.
619      */

620     public boolean getUseFillPaint() {
621         return this.useFillPaint;
622     }
623     
624     /**
625      * Sets the flag that controls whether the fill paint is used to fill
626      * shapes, and sends a {@link RendererChangeEvent} to all
627      * registered listeners.
628      *
629      * @param flag the flag.
630      */

631     public void setUseFillPaint(boolean flag) {
632         this.useFillPaint = flag;
633         notifyListeners(new RendererChangeEvent(this));
634     }
635     
636     /**
637      * Returns <code>true</code> if the renderer should use the outline paint
638      * setting to draw shape outlines, and <code>false</code> if it should just
639      * use the regular paint.
640      *
641      * @return A boolean.
642      */

643     public boolean getUseOutlinePaint() {
644         return this.useOutlinePaint;
645     }
646     
647     /**
648      * Sets the flag that controls whether the outline paint is used to draw
649      * shape outlines, and sends a {@link RendererChangeEvent} to all
650      * registered listeners.
651      *
652      * @param flag the flag.
653      */

654     public void setUseOutlinePaint(boolean flag) {
655         this.useOutlinePaint = flag;
656         notifyListeners(new RendererChangeEvent(this));
657     }
658     
659     /**
660      * Records the state for the renderer. This is used to preserve state
661      * information between calls to the drawItem() method for a single chart
662      * drawing.
663      */

664     public static class State extends XYItemRendererState {
665         
666         /** The path for the current series. */
667         public GeneralPath JavaDoc seriesPath;
668         
669         /**
670          * A flag that indicates if the last (x, y) point was 'good'
671          * (non-null).
672          */

673         private boolean lastPointGood;
674         
675         /**
676          * Creates a new state instance.
677          *
678          * @param info the plot rendering info.
679          */

680         public State(PlotRenderingInfo info) {
681             super(info);
682         }
683         
684         /**
685          * Returns a flag that indicates if the last point drawn (in the
686          * current series) was 'good' (non-null).
687          *
688          * @return A boolean.
689          */

690         public boolean isLastPointGood() {
691             return this.lastPointGood;
692         }
693         
694         /**
695          * Sets a flag that indicates if the last point drawn (in the current
696          * series) was 'good' (non-null).
697          *
698          * @param good the flag.
699          */

700         public void setLastPointGood(boolean good) {
701             this.lastPointGood = good;
702         }
703     }
704     
705     /**
706      * Initialises the renderer.
707      * <P>
708      * This method will be called before the first item is rendered, giving the
709      * renderer an opportunity to initialise any state information it wants to
710      * maintain. The renderer can do nothing if it chooses.
711      *
712      * @param g2 the graphics device.
713      * @param dataArea the area inside the axes.
714      * @param plot the plot.
715      * @param data the data.
716      * @param info an optional info collection object to return data back to
717      * the caller.
718      *
719      * @return The renderer state.
720      */

721     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
722                                           Rectangle2D JavaDoc dataArea,
723                                           XYPlot plot,
724                                           XYDataset data,
725                                           PlotRenderingInfo info) {
726
727         State state = new State(info);
728         state.seriesPath = new GeneralPath JavaDoc();
729         return state;
730
731     }
732     
733     /**
734      * Draws the visual representation of a single data item.
735      *
736      * @param g2 the graphics device.
737      * @param state the renderer state.
738      * @param dataArea the area within which the data is being drawn.
739      * @param info collects information about the drawing.
740      * @param plot the plot (can be used to obtain standard color
741      * information etc).
742      * @param domainAxis the domain axis.
743      * @param rangeAxis the range axis.
744      * @param dataset the dataset.
745      * @param series the series index (zero-based).
746      * @param item the item index (zero-based).
747      * @param crosshairState crosshair information for the plot
748      * (<code>null</code> permitted).
749      * @param pass the pass index.
750      */

751     public void drawItem(Graphics2D JavaDoc g2,
752                          XYItemRendererState state,
753                          Rectangle2D JavaDoc dataArea,
754                          PlotRenderingInfo info,
755                          XYPlot plot,
756                          ValueAxis domainAxis,
757                          ValueAxis rangeAxis,
758                          XYDataset dataset,
759                          int series,
760                          int item,
761                          CrosshairState crosshairState,
762                          int pass) {
763
764         // do nothing if item is not visible
765
if (!getItemVisible(series, item)) {
766             return;
767         }
768
769         // first pass draws the background (lines, for instance)
770
if (isLinePass(pass)) {
771             if (item == 0) {
772                 if (this.drawSeriesLineAsPath) {
773                     State s = (State) state;
774                     s.seriesPath.reset();
775                     s.lastPointGood = false;
776                 }
777             }
778
779             if (getItemLineVisible(series, item)) {
780                 if (this.drawSeriesLineAsPath) {
781                     drawPrimaryLineAsPath(state, g2, plot, dataset, pass,
782                             series, item, domainAxis, rangeAxis, dataArea);
783                 }
784                 else {
785                     drawPrimaryLine(state, g2, plot, dataset, pass, series,
786                             item, domainAxis, rangeAxis, dataArea);
787                 }
788             }
789         }
790         // second pass adds shapes where the items are ..
791
else if (isItemPass(pass)) {
792
793             // setup for collecting optional entity info...
794
EntityCollection entities = null;
795             if (info != null) {
796                 entities = info.getOwner().getEntityCollection();
797             }
798
799             drawSecondaryPass(g2, plot, dataset, pass, series, item,
800                     domainAxis, dataArea, rangeAxis, crosshairState, entities);
801         }
802     }
803
804     /**
805      * Returns <code>true</code> if the specified pass is the one for drawing
806      * lines.
807      *
808      * @param pass the pass.
809      *
810      * @return A boolean.
811      */

812     protected boolean isLinePass(int pass) {
813         return pass == 0;
814     }
815
816     /**
817      * Returns <code>true</code> if the specified pass is the one for drawing
818      * items.
819      *
820      * @param pass the pass.
821      *
822      * @return A boolean.
823      */

824     protected boolean isItemPass(int pass) {
825         return pass == 1;
826     }
827
828     /**
829      * Draws the item (first pass). This method draws the lines
830      * connecting the items.
831      *
832      * @param g2 the graphics device.
833      * @param state the renderer state.
834      * @param dataArea the area within which the data is being drawn.
835      * @param plot the plot (can be used to obtain standard color
836      * information etc).
837      * @param domainAxis the domain axis.
838      * @param rangeAxis the range axis.
839      * @param dataset the dataset.
840      * @param pass the pass.
841      * @param series the series index (zero-based).
842      * @param item the item index (zero-based).
843      */

844     protected void drawPrimaryLine(XYItemRendererState state,
845                                    Graphics2D JavaDoc g2,
846                                    XYPlot plot,
847                                    XYDataset dataset,
848                                    int pass,
849                                    int series,
850                                    int item,
851                                    ValueAxis domainAxis,
852                                    ValueAxis rangeAxis,
853                                    Rectangle2D JavaDoc dataArea) {
854         if (item == 0) {
855             return;
856         }
857
858         // get the data point...
859
double x1 = dataset.getXValue(series, item);
860         double y1 = dataset.getYValue(series, item);
861         if (Double.isNaN(y1) || Double.isNaN(x1)) {
862             return;
863         }
864
865         double x0 = dataset.getXValue(series, item - 1);
866         double y0 = dataset.getYValue(series, item - 1);
867         if (Double.isNaN(y0) || Double.isNaN(x0)) {
868             return;
869         }
870
871         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
872         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
873
874         double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation);
875         double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation);
876
877         double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
878         double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
879
880         // only draw if we have good values
881
if (Double.isNaN(transX0) || Double.isNaN(transY0)
882             || Double.isNaN(transX1) || Double.isNaN(transY1)) {
883             return;
884         }
885
886         PlotOrientation orientation = plot.getOrientation();
887         if (orientation == PlotOrientation.HORIZONTAL) {
888             state.workingLine.setLine(transY0, transX0, transY1, transX1);
889         }
890         else if (orientation == PlotOrientation.VERTICAL) {
891             state.workingLine.setLine(transX0, transY0, transX1, transY1);
892         }
893
894         if (state.workingLine.intersects(dataArea)) {
895             drawFirstPassShape(g2, pass, series, item, state.workingLine);
896         }
897     }
898
899     /**
900      * Draws the first pass shape.
901      *
902      * @param g2 the graphics device.
903      * @param pass the pass.
904      * @param series the series index.
905      * @param item the item index.
906      * @param shape the shape.
907      */

908     protected void drawFirstPassShape(Graphics2D JavaDoc g2, int pass, int series,
909                                       int item, Shape JavaDoc shape) {
910         g2.setStroke(getItemStroke(series, item));
911         g2.setPaint(getItemPaint(series, item));
912         g2.draw(shape);
913     }
914
915
916     /**
917      * Draws the item (first pass). This method draws the lines
918      * connecting the items. Instead of drawing separate lines,
919      * a GeneralPath is constructed and drawn at the end of
920      * the series painting.
921      *
922      * @param g2 the graphics device.
923      * @param state the renderer state.
924      * @param plot the plot (can be used to obtain standard color information
925      * etc).
926      * @param dataset the dataset.
927      * @param pass the pass.
928      * @param series the series index (zero-based).
929      * @param item the item index (zero-based).
930      * @param domainAxis the domain axis.
931      * @param rangeAxis the range axis.
932      * @param dataArea the area within which the data is being drawn.
933      */

934     protected void drawPrimaryLineAsPath(XYItemRendererState state,
935                                          Graphics2D JavaDoc g2, XYPlot plot,
936                                          XYDataset dataset,
937                                          int pass,
938                                          int series,
939                                          int item,
940                                          ValueAxis domainAxis,
941                                          ValueAxis rangeAxis,
942                                          Rectangle2D JavaDoc dataArea) {
943
944
945         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
946         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
947
948         // get the data point...
949
double x1 = dataset.getXValue(series, item);
950         double y1 = dataset.getYValue(series, item);
951         double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
952         double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
953
954         State s = (State) state;
955         // update path to reflect latest point
956
if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) {
957             float x = (float) transX1;
958             float y = (float) transY1;
959             PlotOrientation orientation = plot.getOrientation();
960             if (orientation == PlotOrientation.HORIZONTAL) {
961                 x = (float) transY1;
962                 y = (float) transX1;
963             }
964             if (s.isLastPointGood()) {
965                 s.seriesPath.lineTo(x, y);
966             }
967             else {
968                 s.seriesPath.moveTo(x, y);
969             }
970             s.setLastPointGood(true);
971         }
972         else {
973             s.setLastPointGood(false);
974         }
975         // if this is the last item, draw the path ...
976
if (item == dataset.getItemCount(series) - 1) {
977             // draw path
978
drawFirstPassShape(g2, pass, series, item, s.seriesPath);
979         }
980     }
981
982     /**
983      * Draws the item shapes and adds chart entities (second pass). This method
984      * draws the shapes which mark the item positions. If <code>entities</code>
985      * is not <code>null</code> it will be populated with entity information.
986      *
987      * @param g2 the graphics device.
988      * @param dataArea the area within which the data is being drawn.
989      * @param plot the plot (can be used to obtain standard color
990      * information etc).
991      * @param domainAxis the domain axis.
992      * @param rangeAxis the range axis.
993      * @param dataset the dataset.
994      * @param pass the pass.
995      * @param series the series index (zero-based).
996      * @param item the item index (zero-based).
997      * @param crosshairState the crosshair state.
998      * @param entities the entity collection.
999      */

1000    protected void drawSecondaryPass(Graphics2D JavaDoc g2, XYPlot plot,
1001                                     XYDataset dataset,
1002                                     int pass, int series, int item,
1003                                     ValueAxis domainAxis,
1004                                     Rectangle2D JavaDoc dataArea,
1005                                     ValueAxis rangeAxis,
1006                                     CrosshairState crosshairState,
1007                                     EntityCollection entities) {
1008
1009        Shape JavaDoc entityArea = null;
1010
1011        // get the data point...
1012
double x1 = dataset.getXValue(series, item);
1013        double y1 = dataset.getYValue(series, item);
1014        if (Double.isNaN(y1) || Double.isNaN(x1)) {
1015            return;
1016        }
1017
1018        PlotOrientation orientation = plot.getOrientation();
1019        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
1020        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
1021        double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
1022        double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
1023
1024        if (getItemShapeVisible(series, item)) {
1025            Shape JavaDoc shape = getItemShape(series, item);
1026            if (orientation == PlotOrientation.HORIZONTAL) {
1027                shape = ShapeUtilities.createTranslatedShape(shape, transY1,
1028                        transX1);
1029            }
1030            else if (orientation == PlotOrientation.VERTICAL) {
1031                shape = ShapeUtilities.createTranslatedShape(shape, transX1,
1032                        transY1);
1033            }
1034            entityArea = shape;
1035            if (shape.intersects(dataArea)) {
1036                if (getItemShapeFilled(series, item)) {
1037                    if (this.useFillPaint) {
1038                        g2.setPaint(getItemFillPaint(series, item));
1039                    }
1040                    else {
1041                        g2.setPaint(getItemPaint(series, item));
1042                    }
1043                    g2.fill(shape);
1044                }
1045                if (this.drawOutlines) {
1046                    if (getUseOutlinePaint()) {
1047                        g2.setPaint(getItemOutlinePaint(series, item));
1048                    }
1049                    else {
1050                        g2.setPaint(getItemPaint(series, item));
1051                    }
1052                    g2.setStroke(getItemOutlineStroke(series, item));
1053                    g2.draw(shape);
1054                }
1055            }
1056        }
1057
1058        // draw the item label if there is one...
1059
if (isItemLabelVisible(series, item)) {
1060            double xx = transX1;
1061            double yy = transY1;
1062            if (orientation == PlotOrientation.HORIZONTAL) {
1063                xx = transY1;
1064                yy = transX1;
1065            }
1066            drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
1067                    (y1 < 0.0));
1068        }
1069
1070        updateCrosshairValues(crosshairState, x1, y1, transX1, transY1,
1071                plot.getOrientation());
1072
1073        // add an entity for the item...
1074
if (entities != null) {
1075            addEntity(entities, entityArea, dataset, series, item, transX1,
1076                    transY1);
1077        }
1078    }
1079
1080
1081    /**
1082     * Returns a legend item for the specified series.
1083     *
1084     * @param datasetIndex the dataset index (zero-based).
1085     * @param series the series index (zero-based).
1086     *
1087     * @return A legend item for the series.
1088     */

1089    public LegendItem getLegendItem(int datasetIndex, int series) {
1090
1091        XYPlot plot = getPlot();
1092        if (plot == null) {
1093            return null;
1094        }
1095
1096        LegendItem result = null;
1097        XYDataset dataset = plot.getDataset(datasetIndex);
1098        if (dataset != null) {
1099            if (getItemVisible(series, 0)) {
1100                String JavaDoc label = getLegendItemLabelGenerator().generateLabel(
1101                        dataset, series);
1102                String JavaDoc description = label;
1103                String JavaDoc toolTipText = null;
1104                if (getLegendItemToolTipGenerator() != null) {
1105                    toolTipText = getLegendItemToolTipGenerator().generateLabel(
1106                            dataset, series);
1107                }
1108                String JavaDoc urlText = null;
1109                if (getLegendItemURLGenerator() != null) {
1110                    urlText = getLegendItemURLGenerator().generateLabel(
1111                            dataset, series);
1112                }
1113                boolean shapeIsVisible = getItemShapeVisible(series, 0);
1114                Shape JavaDoc shape = getSeriesShape(series);
1115                boolean shapeIsFilled = getItemShapeFilled(series, 0);
1116                Paint JavaDoc fillPaint = (this.useFillPaint
1117                    ? getSeriesFillPaint(series) : getSeriesPaint(series));
1118                boolean shapeOutlineVisible = this.drawOutlines;
1119                Paint JavaDoc outlinePaint = (this.useOutlinePaint
1120                    ? getSeriesOutlinePaint(series)
1121                    : getSeriesPaint(series));
1122                Stroke JavaDoc outlineStroke = getSeriesOutlineStroke(series);
1123                boolean lineVisible = getItemLineVisible(series, 0);
1124                Stroke JavaDoc lineStroke = getSeriesStroke(series);
1125                Paint JavaDoc linePaint = getSeriesPaint(series);
1126                result = new LegendItem(label, description, toolTipText,
1127                        urlText, shapeIsVisible, shape, shapeIsFilled,
1128                        fillPaint, shapeOutlineVisible, outlinePaint,
1129                        outlineStroke, lineVisible, this.legendLine,
1130                        lineStroke, linePaint);
1131                result.setSeriesIndex(series);
1132                result.setDatasetIndex(datasetIndex);
1133            }
1134        }
1135
1136        return result;
1137
1138    }
1139    
1140    /**
1141     * Returns a clone of the renderer.
1142     *
1143     * @return A clone.
1144     *
1145     * @throws CloneNotSupportedException if the clone cannot be created.
1146     */

1147    public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
1148        return super.clone();
1149    }
1150    
1151    /**
1152     * Tests this renderer for equality with another object.
1153     *
1154     * @param obj the object.
1155     *
1156     * @return <code>true</code> or <code>false</code>.
1157     */

1158    public boolean equals(Object JavaDoc obj) {
1159
1160        if (obj == this) {
1161            return true;
1162        }
1163        if (!(obj instanceof XYLineAndShapeRenderer)) {
1164            return false;
1165        }
1166        if (!super.equals(obj)) {
1167            return false;
1168        }
1169        XYLineAndShapeRenderer that = (XYLineAndShapeRenderer) obj;
1170        if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
1171            return false;
1172        }
1173        if (!ObjectUtilities.equal(
1174            this.seriesLinesVisible, that.seriesLinesVisible)
1175        ) {
1176            return false;
1177        }
1178        if (this.baseLinesVisible != that.baseLinesVisible) {
1179            return false;
1180        }
1181        if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
1182            return false;
1183        }
1184        if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
1185            return false;
1186        }
1187        if (!ObjectUtilities.equal(
1188            this.seriesShapesVisible, that.seriesShapesVisible)
1189        ) {
1190            return false;
1191        }
1192        if (this.baseShapesVisible != that.baseShapesVisible) {
1193            return false;
1194        }
1195        if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
1196            return false;
1197        }
1198        if (!ObjectUtilities.equal(
1199            this.seriesShapesFilled, that.seriesShapesFilled)
1200        ) {
1201            return false;
1202        }
1203        if (this.baseShapesFilled != that.baseShapesFilled) {
1204            return false;
1205        }
1206        if (this.drawOutlines != that.drawOutlines) {
1207            return false;
1208        }
1209        if (this.useOutlinePaint != that.useOutlinePaint) {
1210            return false;
1211        }
1212
1213        return true;
1214
1215    }
1216    
1217    /**
1218     * Provides serialization support.
1219     *
1220     * @param stream the input stream.
1221     *
1222     * @throws IOException if there is an I/O error.
1223     * @throws ClassNotFoundException if there is a classpath problem.
1224     */

1225    private void readObject(ObjectInputStream JavaDoc stream)
1226            throws IOException JavaDoc, ClassNotFoundException JavaDoc {
1227        stream.defaultReadObject();
1228        this.legendLine = SerialUtilities.readShape(stream);
1229    }
1230    
1231    /**
1232     * Provides serialization support.
1233     *
1234     * @param stream the output stream.
1235     *
1236     * @throws IOException if there is an I/O error.
1237     */

1238    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
1239        stream.defaultWriteObject();
1240        SerialUtilities.writeShape(this.legendLine, stream);
1241    }
1242  
1243}
1244
Popular Tags