001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * -------------------------
028     * LineAndShapeRenderer.java
029     * -------------------------
030     * (C) Copyright 2001-2006, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   Mark Watson (www.markwatson.com);
034     *                   Jeremy Bowman;
035     *                   Richard Atkinson;
036     *                   Christian W. Zuckschwerdt;
037     *
038     * $Id: LineAndShapeRenderer.java,v 1.18.2.7 2006/10/06 15:02:16 mungady Exp $
039     *
040     * Changes
041     * -------
042     * 23-Oct-2001 : Version 1 (DG);
043     * 15-Nov-2001 : Modified to allow for null data values (DG);
044     * 16-Jan-2002 : Renamed HorizontalCategoryItemRenderer.java 
045     *               --> CategoryItemRenderer.java (DG);
046     * 05-Feb-2002 : Changed return type of the drawCategoryItem method from void 
047     *               to Shape, as part of the tooltips implementation (DG);
048     * 11-May-2002 : Support for value label drawing (JB);
049     * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
050     * 25-Jun-2002 : Removed redundant import (DG);
051     * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs 
052     *               for HTML image maps (RA);
053     * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
054     * 11-Oct-2002 : Added new constructor to incorporate tool tip and URL 
055     *               generators (DG);
056     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 
057     *               CategoryToolTipGenerator interface (DG);
058     * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
059     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis 
060     *               for category spacing (DG);
061     * 17-Jan-2003 : Moved plot classes to a separate package (DG);
062     * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in drawItem()
063     *               method (DG);
064     * 12-May-2003 : Modified to take into account the plot orientation (DG);
065     * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
066     * 30-Jul-2003 : Modified entity constructor (CZ);
067     * 22-Sep-2003 : Fixed cloning (DG);
068     * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste 
069     *               override easier (DG);
070     * 16-Jun-2004 : Fixed bug (id=972454) with label positioning on horizontal 
071     *               charts (DG);
072     * 15-Oct-2004 : Updated equals() method (DG);
073     * 05-Nov-2004 : Modified drawItem() signature (DG);
074     * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
075     * 27-Jan-2005 : Changed attribute names, modified constructor and removed 
076     *               constants (DG);
077     * 01-Feb-2005 : Removed unnecessary constants (DG);
078     * 15-Mar-2005 : Fixed bug 1163897, concerning outlines for shapes (DG);
079     * 13-Apr-2005 : Check flags that control series visibility (DG);
080     * 20-Apr-2005 : Use generators for legend labels, tooltips and URLs (DG);
081     * 09-Jun-2005 : Use addItemEntity() method (DG);
082     * ------------- JFREECHART 1.0.x ---------------------------------------------
083     * 25-May-2006 : Added check to drawItem() to detect when both the line and
084     *               the shape are not visible (DG);
085     * 
086     */
087    
088    package org.jfree.chart.renderer.category;
089    
090    import java.awt.Graphics2D;
091    import java.awt.Paint;
092    import java.awt.Shape;
093    import java.awt.Stroke;
094    import java.awt.geom.Line2D;
095    import java.awt.geom.Rectangle2D;
096    import java.io.Serializable;
097    
098    import org.jfree.chart.LegendItem;
099    import org.jfree.chart.axis.CategoryAxis;
100    import org.jfree.chart.axis.ValueAxis;
101    import org.jfree.chart.entity.EntityCollection;
102    import org.jfree.chart.event.RendererChangeEvent;
103    import org.jfree.chart.plot.CategoryPlot;
104    import org.jfree.chart.plot.PlotOrientation;
105    import org.jfree.data.category.CategoryDataset;
106    import org.jfree.util.BooleanList;
107    import org.jfree.util.BooleanUtilities;
108    import org.jfree.util.ObjectUtilities;
109    import org.jfree.util.PublicCloneable;
110    import org.jfree.util.ShapeUtilities;
111    
112    /**
113     * A renderer that draws shapes for each data item, and lines between data 
114     * items (for use with the {@link CategoryPlot} class).
115     */
116    public class LineAndShapeRenderer extends AbstractCategoryItemRenderer 
117                                      implements Cloneable, PublicCloneable, 
118                                                 Serializable {
119    
120        /** For serialization. */
121        private static final long serialVersionUID = -197749519869226398L;
122        
123        /** A flag that controls whether or not lines are visible for ALL series. */
124        private Boolean linesVisible;
125    
126        /** 
127         * A table of flags that control (per series) whether or not lines are 
128         * visible. 
129         */
130        private BooleanList seriesLinesVisible;
131    
132        /** 
133         * A flag indicating whether or not lines are drawn between non-null 
134         * points. 
135         */
136        private boolean baseLinesVisible;
137    
138        /** 
139         * A flag that controls whether or not shapes are visible for ALL series. 
140         */
141        private Boolean shapesVisible;
142    
143        /** 
144         * A table of flags that control (per series) whether or not shapes are 
145         * visible. 
146         */
147        private BooleanList seriesShapesVisible;
148    
149        /** The default value returned by the getShapeVisible() method. */
150        private boolean baseShapesVisible;
151    
152        /** A flag that controls whether or not shapes are filled for ALL series. */
153        private Boolean shapesFilled;
154        
155        /** 
156         * A table of flags that control (per series) whether or not shapes are 
157         * filled. 
158         */
159        private BooleanList seriesShapesFilled;
160        
161        /** The default value returned by the getShapeFilled() method. */
162        private boolean baseShapesFilled;
163        
164        /** 
165         * A flag that controls whether the fill paint is used for filling 
166         * shapes. 
167         */
168        private boolean useFillPaint;
169    
170        /** A flag that controls whether outlines are drawn for shapes. */
171        private boolean drawOutlines;
172            
173        /** 
174         * A flag that controls whether the outline paint is used for drawing shape 
175         * outlines - if not, the regular series paint is used. 
176         */
177        private boolean useOutlinePaint;
178    
179        /**
180         * Creates a renderer with both lines and shapes visible by default.
181         */
182        public LineAndShapeRenderer() {
183            this(true, true);
184        }
185    
186        /**
187         * Creates a new renderer with lines and/or shapes visible.
188         * 
189         * @param lines  draw lines?
190         * @param shapes  draw shapes?
191         */
192        public LineAndShapeRenderer(boolean lines, boolean shapes) {
193            super();
194            this.linesVisible = null;
195            this.seriesLinesVisible = new BooleanList();
196            this.baseLinesVisible = lines;
197            this.shapesVisible = null;
198            this.seriesShapesVisible = new BooleanList();
199            this.baseShapesVisible = shapes;
200            this.shapesFilled = null;
201            this.seriesShapesFilled = new BooleanList();
202            this.baseShapesFilled = true;
203            this.useFillPaint = false;
204            this.drawOutlines = true;
205            this.useOutlinePaint = false;
206        }
207        
208        // LINES VISIBLE
209    
210        /**
211         * Returns the flag used to control whether or not the line for an item is 
212         * visible.
213         *
214         * @param series  the series index (zero-based).
215         * @param item  the item index (zero-based).
216         *
217         * @return A boolean.
218         */
219        public boolean getItemLineVisible(int series, int item) {
220            Boolean flag = this.linesVisible;
221            if (flag == null) {
222                flag = getSeriesLinesVisible(series);
223            }
224            if (flag != null) {
225                return flag.booleanValue();
226            }
227            else {
228                return this.baseLinesVisible;   
229            }
230        }
231    
232        /**
233         * Returns a flag that controls whether or not lines are drawn for ALL 
234         * series.  If this flag is <code>null</code>, then the "per series" 
235         * settings will apply.
236         * 
237         * @return A flag (possibly <code>null</code>).
238         */
239        public Boolean getLinesVisible() {
240            return this.linesVisible;   
241        }
242        
243        /**
244         * Sets a flag that controls whether or not lines are drawn between the 
245         * items in ALL series, and sends a {@link RendererChangeEvent} to all 
246         * registered listeners.  You need to set this to <code>null</code> if you 
247         * want the "per series" settings to apply.
248         *
249         * @param visible  the flag (<code>null</code> permitted).
250         */
251        public void setLinesVisible(Boolean visible) {
252            this.linesVisible = visible;
253            notifyListeners(new RendererChangeEvent(this));
254        }
255    
256        /**
257         * Sets a flag that controls whether or not lines are drawn between the 
258         * items in ALL series, and sends a {@link RendererChangeEvent} to all 
259         * registered listeners.
260         *
261         * @param visible  the flag.
262         */
263        public void setLinesVisible(boolean visible) {
264            setLinesVisible(BooleanUtilities.valueOf(visible));
265        }
266    
267        /**
268         * Returns the flag used to control whether or not the lines for a series 
269         * are visible.
270         *
271         * @param series  the series index (zero-based).
272         *
273         * @return The flag (possibly <code>null</code>).
274         */
275        public Boolean getSeriesLinesVisible(int series) {
276            return this.seriesLinesVisible.getBoolean(series);
277        }
278    
279        /**
280         * Sets the 'lines visible' flag for a series.
281         *
282         * @param series  the series index (zero-based).
283         * @param flag  the flag (<code>null</code> permitted).
284         */
285        public void setSeriesLinesVisible(int series, Boolean flag) {
286            this.seriesLinesVisible.setBoolean(series, flag);
287            notifyListeners(new RendererChangeEvent(this));
288        }
289    
290        /**
291         * Sets the 'lines visible' flag for a series.
292         * 
293         * @param series  the series index (zero-based).
294         * @param visible  the flag.
295         */
296        public void setSeriesLinesVisible(int series, boolean visible) {
297            setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
298        }
299        
300        /**
301         * Returns the base 'lines visible' attribute.
302         *
303         * @return The base flag.
304         */
305        public boolean getBaseLinesVisible() {
306            return this.baseLinesVisible;
307        }
308    
309        /**
310         * Sets the base 'lines visible' flag.
311         *
312         * @param flag  the flag.
313         */
314        public void setBaseLinesVisible(boolean flag) {
315            this.baseLinesVisible = flag;
316            notifyListeners(new RendererChangeEvent(this));
317        }
318    
319        // SHAPES VISIBLE
320    
321        /**
322         * Returns the flag used to control whether or not the shape for an item is 
323         * visible.
324         *
325         * @param series  the series index (zero-based).
326         * @param item  the item index (zero-based).
327         *
328         * @return A boolean.
329         */
330        public boolean getItemShapeVisible(int series, int item) {
331            Boolean flag = this.shapesVisible;
332            if (flag == null) {
333                flag = getSeriesShapesVisible(series);
334            }
335            if (flag != null) {
336                return flag.booleanValue();
337            }
338            else {
339                return this.baseShapesVisible;   
340            }
341        }
342    
343        /**
344         * Returns the flag that controls whether the shapes are visible for the 
345         * items in ALL series.
346         * 
347         * @return The flag (possibly <code>null</code>).
348         */
349        public Boolean getShapesVisible() {
350            return this.shapesVisible;    
351        }
352        
353        /**
354         * Sets the 'shapes visible' for ALL series and sends a 
355         * {@link RendererChangeEvent} to all registered listeners.
356         *
357         * @param visible  the flag (<code>null</code> permitted).
358         */
359        public void setShapesVisible(Boolean visible) {
360            this.shapesVisible = visible;
361            notifyListeners(new RendererChangeEvent(this));
362        }
363    
364        /**
365         * Sets the 'shapes visible' for ALL series and sends a 
366         * {@link RendererChangeEvent} to all registered listeners.
367         * 
368         * @param visible  the flag.
369         */
370        public void setShapesVisible(boolean visible) {
371            setShapesVisible(BooleanUtilities.valueOf(visible));
372        }
373    
374        /**
375         * Returns the flag used to control whether or not the shapes for a series
376         * are visible.
377         *
378         * @param series  the series index (zero-based).
379         *
380         * @return A boolean.
381         */
382        public Boolean getSeriesShapesVisible(int series) {
383            return this.seriesShapesVisible.getBoolean(series);
384        }
385    
386        /**
387         * Sets the 'shapes visible' flag for a series and sends a 
388         * {@link RendererChangeEvent} to all registered listeners.
389         * 
390         * @param series  the series index (zero-based).
391         * @param visible  the flag.
392         */
393        public void setSeriesShapesVisible(int series, boolean visible) {
394            setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
395        }
396        
397        /**
398         * Sets the 'shapes visible' flag for a series and sends a 
399         * {@link RendererChangeEvent} to all registered listeners.
400         *
401         * @param series  the series index (zero-based).
402         * @param flag  the flag.
403         */
404        public void setSeriesShapesVisible(int series, Boolean flag) {
405            this.seriesShapesVisible.setBoolean(series, flag);
406            notifyListeners(new RendererChangeEvent(this));
407        }
408    
409        /**
410         * Returns the base 'shape visible' attribute.
411         *
412         * @return The base flag.
413         */
414        public boolean getBaseShapesVisible() {
415            return this.baseShapesVisible;
416        }
417    
418        /**
419         * Sets the base 'shapes visible' flag.
420         *
421         * @param flag  the flag.
422         */
423        public void setBaseShapesVisible(boolean flag) {
424            this.baseShapesVisible = flag;
425            notifyListeners(new RendererChangeEvent(this));
426        }
427    
428        /**
429         * Returns <code>true</code> if outlines should be drawn for shapes, and 
430         * <code>false</code> otherwise.
431         * 
432         * @return A boolean.
433         */
434        public boolean getDrawOutlines() {
435            return this.drawOutlines;
436        }
437        
438        /**
439         * Sets the flag that controls whether outlines are drawn for 
440         * shapes, and sends a {@link RendererChangeEvent} to all registered 
441         * listeners. 
442         * <P>
443         * In some cases, shapes look better if they do NOT have an outline, but 
444         * this flag allows you to set your own preference.
445         * 
446         * @param flag  the flag.
447         */
448        public void setDrawOutlines(boolean flag) {
449            this.drawOutlines = flag;
450            notifyListeners(new RendererChangeEvent(this));
451        }
452        
453        /**
454         * Returns the flag that controls whether the outline paint is used for 
455         * shape outlines.  If not, the regular series paint is used.
456         * 
457         * @return A boolean.
458         */
459        public boolean getUseOutlinePaint() {
460            return this.useOutlinePaint;   
461        }
462        
463        /**
464         * Sets the flag that controls whether the outline paint is used for shape 
465         * outlines.
466         * 
467         * @param use  the flag.
468         */
469        public void setUseOutlinePaint(boolean use) {
470            this.useOutlinePaint = use;   
471        }
472    
473        // SHAPES FILLED
474        
475        /**
476         * Returns the flag used to control whether or not the shape for an item 
477         * is filled. The default implementation passes control to the 
478         * <code>getSeriesShapesFilled</code> method. You can override this method
479         * if you require different behaviour.
480         *
481         * @param series  the series index (zero-based).
482         * @param item  the item index (zero-based).
483         *
484         * @return A boolean.
485         */
486        public boolean getItemShapeFilled(int series, int item) {
487            return getSeriesShapesFilled(series);
488        }
489    
490        /**
491         * Returns the flag used to control whether or not the shapes for a series 
492         * are filled. 
493         *
494         * @param series  the series index (zero-based).
495         *
496         * @return A boolean.
497         */
498        public boolean getSeriesShapesFilled(int series) {
499    
500            // return the overall setting, if there is one...
501            if (this.shapesFilled != null) {
502                return this.shapesFilled.booleanValue();
503            }
504    
505            // otherwise look up the paint table
506            Boolean flag = this.seriesShapesFilled.getBoolean(series);
507            if (flag != null) {
508                return flag.booleanValue();
509            }
510            else {
511                return this.baseShapesFilled;
512            } 
513    
514        }
515        
516        /**
517         * Returns the flag that controls whether or not shapes are filled for 
518         * ALL series.
519         * 
520         * @return A Boolean.
521         */
522        public Boolean getShapesFilled() {
523            return this.shapesFilled;
524        }
525    
526        /**
527         * Sets the 'shapes filled' for ALL series.
528         * 
529         * @param filled  the flag.
530         */
531        public void setShapesFilled(boolean filled) {
532            if (filled) {
533                setShapesFilled(Boolean.TRUE);
534            }
535            else {
536                setShapesFilled(Boolean.FALSE);
537            }
538        }
539        
540        /**
541         * Sets the 'shapes filled' for ALL series.
542         * 
543         * @param filled  the flag (<code>null</code> permitted).
544         */
545        public void setShapesFilled(Boolean filled) {
546            this.shapesFilled = filled;
547        }
548        
549        /**
550         * Sets the 'shapes filled' flag for a series.
551         *
552         * @param series  the series index (zero-based).
553         * @param filled  the flag.
554         */
555        public void setSeriesShapesFilled(int series, Boolean filled) {
556            this.seriesShapesFilled.setBoolean(series, filled);
557        }
558    
559        /**
560         * Sets the 'shapes filled' flag for a series.
561         *
562         * @param series  the series index (zero-based).
563         * @param filled  the flag.
564         */
565        public void setSeriesShapesFilled(int series, boolean filled) {
566            this.seriesShapesFilled.setBoolean(
567                series, BooleanUtilities.valueOf(filled)
568            );
569        }
570    
571        /**
572         * Returns the base 'shape filled' attribute.
573         *
574         * @return The base flag.
575         */
576        public boolean getBaseShapesFilled() {
577            return this.baseShapesFilled;
578        }
579    
580        /**
581         * Sets the base 'shapes filled' flag.
582         *
583         * @param flag  the flag.
584         */
585        public void setBaseShapesFilled(boolean flag) {
586            this.baseShapesFilled = flag;
587        }
588    
589        /**
590         * Returns <code>true</code> if the renderer should use the fill paint 
591         * setting to fill shapes, and <code>false</code> if it should just
592         * use the regular paint.
593         * 
594         * @return A boolean.
595         */
596        public boolean getUseFillPaint() {
597            return this.useFillPaint;
598        }
599        
600        /**
601         * Sets the flag that controls whether the fill paint is used to fill 
602         * shapes, and sends a {@link RendererChangeEvent} to all 
603         * registered listeners.
604         * 
605         * @param flag  the flag.
606         */
607        public void setUseFillPaint(boolean flag) {
608            this.useFillPaint = flag;
609            notifyListeners(new RendererChangeEvent(this));
610        }
611        
612        /**
613         * Returns a legend item for a series.
614         *
615         * @param datasetIndex  the dataset index (zero-based).
616         * @param series  the series index (zero-based).
617         *
618         * @return The legend item.
619         */
620        public LegendItem getLegendItem(int datasetIndex, int series) {
621    
622            CategoryPlot cp = getPlot();
623            if (cp == null) {
624                return null;
625            }
626    
627            if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
628                CategoryDataset dataset;
629                dataset = cp.getDataset(datasetIndex);
630                String label = getLegendItemLabelGenerator().generateLabel(
631                        dataset, series);
632                String description = label;
633                String toolTipText = null; 
634                if (getLegendItemToolTipGenerator() != null) {
635                    toolTipText = getLegendItemToolTipGenerator().generateLabel(
636                            dataset, series);   
637                }
638                String urlText = null;
639                if (getLegendItemURLGenerator() != null) {
640                    urlText = getLegendItemURLGenerator().generateLabel(
641                            dataset, series);   
642                }
643                Shape shape = getSeriesShape(series);
644                Paint paint = getSeriesPaint(series);
645                Paint fillPaint = (this.useFillPaint 
646                        ? getItemFillPaint(series, 0) : paint);
647                boolean shapeOutlineVisible = this.drawOutlines;
648                Paint outlinePaint = (this.useOutlinePaint 
649                        ? getItemOutlinePaint(series, 0) : paint);
650                Stroke outlineStroke = getSeriesOutlineStroke(series);
651                boolean lineVisible = getItemLineVisible(series, 0);
652                boolean shapeVisible = getItemShapeVisible(series, 0);
653                return new LegendItem(label, description, toolTipText, 
654                        urlText, shapeVisible, shape, getItemShapeFilled(series, 0),
655                        fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke,
656                        lineVisible, new Line2D.Double(-7.0, 0.0, 7.0, 0.0),
657                        getItemStroke(series, 0), getItemPaint(series, 0));
658            }
659            return null;
660    
661        }
662    
663        /**
664         * This renderer uses two passes to draw the data.
665         * 
666         * @return The pass count (<code>2</code> for this renderer).
667         */
668        public int getPassCount() {
669            return 2;   
670        }
671        
672        /**
673         * Draw a single data item.
674         *
675         * @param g2  the graphics device.
676         * @param state  the renderer state.
677         * @param dataArea  the area in which the data is drawn.
678         * @param plot  the plot.
679         * @param domainAxis  the domain axis.
680         * @param rangeAxis  the range axis.
681         * @param dataset  the dataset.
682         * @param row  the row index (zero-based).
683         * @param column  the column index (zero-based).
684         * @param pass  the pass index.
685         */
686        public void drawItem(Graphics2D g2, CategoryItemRendererState state,
687                Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
688                ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
689                int pass) {
690    
691            // do nothing if item is not visible
692            if (!getItemVisible(row, column)) {
693                return;   
694            }
695            
696            // do nothing if both the line and shape are not visible
697            if (!getItemLineVisible(row, column) 
698                    && !getItemShapeVisible(row, column)) {
699                return;
700            }
701    
702            // nothing is drawn for null...
703            Number v = dataset.getValue(row, column);
704            if (v == null) {
705                return;
706            }
707    
708            PlotOrientation orientation = plot.getOrientation();
709    
710            // current data point...
711            double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), 
712                    dataArea, plot.getDomainAxisEdge());
713            double value = v.doubleValue();
714            double y1 = rangeAxis.valueToJava2D(value, dataArea, 
715                    plot.getRangeAxisEdge());
716    
717            if (pass == 0 && getItemLineVisible(row, column)) {
718                if (column != 0) {
719                    Number previousValue = dataset.getValue(row, column - 1);
720                    if (previousValue != null) {
721                        // previous data point...
722                        double previous = previousValue.doubleValue();
723                        double x0 = domainAxis.getCategoryMiddle(column - 1, 
724                                getColumnCount(), dataArea, 
725                                plot.getDomainAxisEdge());
726                        double y0 = rangeAxis.valueToJava2D(previous, dataArea, 
727                                plot.getRangeAxisEdge());
728    
729                        Line2D line = null;
730                        if (orientation == PlotOrientation.HORIZONTAL) {
731                            line = new Line2D.Double(y0, x0, y1, x1);
732                        }
733                        else if (orientation == PlotOrientation.VERTICAL) {
734                            line = new Line2D.Double(x0, y0, x1, y1);
735                        }
736                        g2.setPaint(getItemPaint(row, column));
737                        g2.setStroke(getItemStroke(row, column));
738                        g2.draw(line);
739                    }
740                }
741            }
742    
743            if (pass == 1) {
744                Shape shape = getItemShape(row, column);
745                if (orientation == PlotOrientation.HORIZONTAL) {
746                    shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
747                }
748                else if (orientation == PlotOrientation.VERTICAL) {
749                    shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
750                }
751    
752                if (getItemShapeVisible(row, column)) {
753                    if (getItemShapeFilled(row, column)) {
754                        if (this.useFillPaint) {
755                            g2.setPaint(getItemFillPaint(row, column));
756                        }
757                        else {
758                            g2.setPaint(getItemPaint(row, column));   
759                        }
760                        g2.fill(shape);
761                    }
762                    if (this.drawOutlines) {
763                        if (this.useOutlinePaint) {
764                            g2.setPaint(getItemOutlinePaint(row, column));   
765                        }
766                        else {
767                            g2.setPaint(getItemPaint(row, column));
768                        }
769                        g2.setStroke(getItemOutlineStroke(row, column));
770                        g2.draw(shape);
771                    }
772                }
773    
774                // draw the item label if there is one...
775                if (isItemLabelVisible(row, column)) {
776                    if (orientation == PlotOrientation.HORIZONTAL) {
777                        drawItemLabel(g2, orientation, dataset, row, column, y1, 
778                                x1, (value < 0.0));
779                    }
780                    else if (orientation == PlotOrientation.VERTICAL) {
781                        drawItemLabel(g2, orientation, dataset, row, column, x1, 
782                                y1, (value < 0.0));
783                    }
784                }
785    
786                // add an item entity, if this information is being collected
787                EntityCollection entities = state.getEntityCollection();
788                if (entities != null) {
789                    addItemEntity(entities, dataset, row, column, shape);
790                }
791            }
792    
793        }
794        
795        /**
796         * Tests this renderer for equality with an arbitrary object.
797         *
798         * @param obj  the object (<code>null</code> permitted).
799         *
800         * @return A boolean.
801         */
802        public boolean equals(Object obj) {
803    
804            if (obj == this) {
805                return true;
806            }
807            if (!(obj instanceof LineAndShapeRenderer)) {
808                return false;
809            }
810            
811            LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
812            if (this.baseLinesVisible != that.baseLinesVisible) {
813                return false;
814            }
815            if (!ObjectUtilities.equal(this.seriesLinesVisible, 
816                    that.seriesLinesVisible)) {
817                return false;
818            }
819            if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
820                return false;
821            }
822            if (this.baseShapesVisible != that.baseShapesVisible) {
823                return false;
824            }
825            if (!ObjectUtilities.equal(this.seriesShapesVisible, 
826                    that.seriesShapesVisible)) {
827                return false;
828            }
829            if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
830                return false;
831            }
832            if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
833                return false;
834            }
835            if (!ObjectUtilities.equal(this.seriesShapesFilled, 
836                    that.seriesShapesFilled)) {
837                return false;
838            }
839            if (this.baseShapesFilled != that.baseShapesFilled) {
840                return false;
841            }
842            if (this.useOutlinePaint != that.useOutlinePaint) {
843                return false;
844            }
845            if (!super.equals(obj)) {
846                return false;
847            }
848            return true;
849        }
850    
851        /**
852         * Returns an independent copy of the renderer.
853         * 
854         * @return A clone.
855         * 
856         * @throws CloneNotSupportedException  should not happen.
857         */
858        public Object clone() throws CloneNotSupportedException {
859            LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone();
860            clone.seriesLinesVisible 
861                = (BooleanList) this.seriesLinesVisible.clone();
862            clone.seriesShapesVisible 
863                = (BooleanList) this.seriesLinesVisible.clone();
864            clone.seriesShapesFilled 
865                = (BooleanList) this.seriesShapesFilled.clone();
866            return clone;
867        }
868        
869    }