001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, 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-2007, 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     * Changes
039     * -------
040     * 23-Oct-2001 : Version 1 (DG);
041     * 15-Nov-2001 : Modified to allow for null data values (DG);
042     * 16-Jan-2002 : Renamed HorizontalCategoryItemRenderer.java
043     *               --> CategoryItemRenderer.java (DG);
044     * 05-Feb-2002 : Changed return type of the drawCategoryItem method from void
045     *               to Shape, as part of the tooltips implementation (DG);
046     * 11-May-2002 : Support for value label drawing (JB);
047     * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
048     * 25-Jun-2002 : Removed redundant import (DG);
049     * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
050     *               for HTML image maps (RA);
051     * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
052     * 11-Oct-2002 : Added new constructor to incorporate tool tip and URL
053     *               generators (DG);
054     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
055     *               CategoryToolTipGenerator interface (DG);
056     * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
057     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
058     *               for category spacing (DG);
059     * 17-Jan-2003 : Moved plot classes to a separate package (DG);
060     * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in drawItem()
061     *               method (DG);
062     * 12-May-2003 : Modified to take into account the plot orientation (DG);
063     * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
064     * 30-Jul-2003 : Modified entity constructor (CZ);
065     * 22-Sep-2003 : Fixed cloning (DG);
066     * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
067     *               override easier (DG);
068     * 16-Jun-2004 : Fixed bug (id=972454) with label positioning on horizontal
069     *               charts (DG);
070     * 15-Oct-2004 : Updated equals() method (DG);
071     * 05-Nov-2004 : Modified drawItem() signature (DG);
072     * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
073     * 27-Jan-2005 : Changed attribute names, modified constructor and removed
074     *               constants (DG);
075     * 01-Feb-2005 : Removed unnecessary constants (DG);
076     * 15-Mar-2005 : Fixed bug 1163897, concerning outlines for shapes (DG);
077     * 13-Apr-2005 : Check flags that control series visibility (DG);
078     * 20-Apr-2005 : Use generators for legend labels, tooltips and URLs (DG);
079     * 09-Jun-2005 : Use addItemEntity() method (DG);
080     * ------------- JFREECHART 1.0.x ---------------------------------------------
081     * 25-May-2006 : Added check to drawItem() to detect when both the line and
082     *               the shape are not visible (DG);
083     * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
084     * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
085     * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
086     * 24-Sep-2007 : Deprecated redundant fields/methods (DG);
087     * 27-Sep-2007 : Added option to offset series x-position within category (DG);
088     *
089     */
090    
091    package org.jfree.chart.renderer.category;
092    
093    import java.awt.Graphics2D;
094    import java.awt.Paint;
095    import java.awt.Shape;
096    import java.awt.Stroke;
097    import java.awt.geom.Line2D;
098    import java.awt.geom.Rectangle2D;
099    import java.io.Serializable;
100    
101    import org.jfree.chart.LegendItem;
102    import org.jfree.chart.axis.CategoryAxis;
103    import org.jfree.chart.axis.ValueAxis;
104    import org.jfree.chart.entity.EntityCollection;
105    import org.jfree.chart.event.RendererChangeEvent;
106    import org.jfree.chart.plot.CategoryPlot;
107    import org.jfree.chart.plot.PlotOrientation;
108    import org.jfree.data.category.CategoryDataset;
109    import org.jfree.util.BooleanList;
110    import org.jfree.util.BooleanUtilities;
111    import org.jfree.util.ObjectUtilities;
112    import org.jfree.util.PublicCloneable;
113    import org.jfree.util.ShapeUtilities;
114    
115    /**
116     * A renderer that draws shapes for each data item, and lines between data
117     * items (for use with the {@link CategoryPlot} class).
118     */
119    public class LineAndShapeRenderer extends AbstractCategoryItemRenderer
120            implements Cloneable, PublicCloneable, Serializable {
121    
122        /** For serialization. */
123        private static final long serialVersionUID = -197749519869226398L;
124    
125        /**
126         * A flag that controls whether or not lines are visible for ALL series.
127         *
128         * @deprecated As of 1.0.7 (this override flag is unnecessary).
129         */
130        private Boolean linesVisible;
131    
132        /**
133         * A table of flags that control (per series) whether or not lines are
134         * visible.
135         */
136        private BooleanList seriesLinesVisible;
137    
138        /**
139         * A flag indicating whether or not lines are drawn between non-null
140         * points.
141         */
142        private boolean baseLinesVisible;
143    
144        /**
145         * A flag that controls whether or not shapes are visible for ALL series.
146         *
147         * @deprecated As of 1.0.7 (this override flag is unnecessary).
148         */
149        private Boolean shapesVisible;
150    
151        /**
152         * A table of flags that control (per series) whether or not shapes are
153         * visible.
154         */
155        private BooleanList seriesShapesVisible;
156    
157        /** The default value returned by the getShapeVisible() method. */
158        private boolean baseShapesVisible;
159    
160        /**
161         * A flag that controls whether or not shapes are filled for ALL series.
162         *
163         * @deprecated As of 1.0.7 (this override flag is unnecessary).
164         */
165        private Boolean shapesFilled;
166    
167        /**
168         * A table of flags that control (per series) whether or not shapes are
169         * filled.
170         */
171        private BooleanList seriesShapesFilled;
172    
173        /** The default value returned by the getShapeFilled() method. */
174        private boolean baseShapesFilled;
175    
176        /**
177         * A flag that controls whether the fill paint is used for filling
178         * shapes.
179         */
180        private boolean useFillPaint;
181    
182        /** A flag that controls whether outlines are drawn for shapes. */
183        private boolean drawOutlines;
184    
185        /**
186         * A flag that controls whether the outline paint is used for drawing shape
187         * outlines - if not, the regular series paint is used.
188         */
189        private boolean useOutlinePaint;
190    
191        /**
192         * A flag that controls whether or not the x-position for each item is
193         * offset within the category according to the series.
194         *
195         * @since 1.0.7
196         */
197        private boolean useSeriesOffset;
198    
199        /**
200         * The item margin used for series offsetting - this allows the positioning
201         * to match the bar positions of the {@link BarRenderer} class.
202         *
203         * @since 1.0.7
204         */
205        private double itemMargin;
206    
207        /**
208         * Creates a renderer with both lines and shapes visible by default.
209         */
210        public LineAndShapeRenderer() {
211            this(true, true);
212        }
213    
214        /**
215         * Creates a new renderer with lines and/or shapes visible.
216         *
217         * @param lines  draw lines?
218         * @param shapes  draw shapes?
219         */
220        public LineAndShapeRenderer(boolean lines, boolean shapes) {
221            super();
222            this.linesVisible = null;
223            this.seriesLinesVisible = new BooleanList();
224            this.baseLinesVisible = lines;
225            this.shapesVisible = null;
226            this.seriesShapesVisible = new BooleanList();
227            this.baseShapesVisible = shapes;
228            this.shapesFilled = null;
229            this.seriesShapesFilled = new BooleanList();
230            this.baseShapesFilled = true;
231            this.useFillPaint = false;
232            this.drawOutlines = true;
233            this.useOutlinePaint = false;
234            this.useSeriesOffset = false;  // preserves old behaviour
235            this.itemMargin = 0.0;
236        }
237    
238        // LINES VISIBLE
239    
240        /**
241         * Returns the flag used to control whether or not the line for an item is
242         * visible.
243         *
244         * @param series  the series index (zero-based).
245         * @param item  the item index (zero-based).
246         *
247         * @return A boolean.
248         */
249        public boolean getItemLineVisible(int series, int item) {
250            Boolean flag = this.linesVisible;
251            if (flag == null) {
252                flag = getSeriesLinesVisible(series);
253            }
254            if (flag != null) {
255                return flag.booleanValue();
256            }
257            else {
258                return this.baseLinesVisible;
259            }
260        }
261    
262        /**
263         * Returns a flag that controls whether or not lines are drawn for ALL
264         * series.  If this flag is <code>null</code>, then the "per series"
265         * settings will apply.
266         *
267         * @return A flag (possibly <code>null</code>).
268         *
269         * @see #setLinesVisible(Boolean)
270         *
271         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
272         *     use the per-series and base (default) settings).
273         */
274        public Boolean getLinesVisible() {
275            return this.linesVisible;
276        }
277    
278        /**
279         * Sets a flag that controls whether or not lines are drawn between the
280         * items in ALL series, and sends a {@link RendererChangeEvent} to all
281         * registered listeners.  You need to set this to <code>null</code> if you
282         * want the "per series" settings to apply.
283         *
284         * @param visible  the flag (<code>null</code> permitted).
285         *
286         * @see #getLinesVisible()
287         *
288         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
289         *     use the per-series and base (default) settings).
290         */
291        public void setLinesVisible(Boolean visible) {
292            this.linesVisible = visible;
293            fireChangeEvent();
294        }
295    
296        /**
297         * Sets a flag that controls whether or not lines are drawn between the
298         * items in ALL series, and sends a {@link RendererChangeEvent} to all
299         * registered listeners.
300         *
301         * @param visible  the flag.
302         *
303         * @see #getLinesVisible()
304         *
305         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
306         *     use the per-series and base (default) settings).
307         */
308        public void setLinesVisible(boolean visible) {
309            setLinesVisible(BooleanUtilities.valueOf(visible));
310        }
311    
312        /**
313         * Returns the flag used to control whether or not the lines for a series
314         * are visible.
315         *
316         * @param series  the series index (zero-based).
317         *
318         * @return The flag (possibly <code>null</code>).
319         *
320         * @see #setSeriesLinesVisible(int, Boolean)
321         */
322        public Boolean getSeriesLinesVisible(int series) {
323            return this.seriesLinesVisible.getBoolean(series);
324        }
325    
326        /**
327         * Sets the 'lines visible' flag for a series and sends a
328         * {@link RendererChangeEvent} to all registered listeners.
329         *
330         * @param series  the series index (zero-based).
331         * @param flag  the flag (<code>null</code> permitted).
332         *
333         * @see #getSeriesLinesVisible(int)
334         */
335        public void setSeriesLinesVisible(int series, Boolean flag) {
336            this.seriesLinesVisible.setBoolean(series, flag);
337            fireChangeEvent();
338        }
339    
340        /**
341         * Sets the 'lines visible' flag for a series and sends a
342         * {@link RendererChangeEvent} to all registered listeners.
343         *
344         * @param series  the series index (zero-based).
345         * @param visible  the flag.
346         *
347         * @see #getSeriesLinesVisible(int)
348         */
349        public void setSeriesLinesVisible(int series, boolean visible) {
350            setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
351        }
352    
353        /**
354         * Returns the base 'lines visible' attribute.
355         *
356         * @return The base flag.
357         *
358         * @see #getBaseLinesVisible()
359         */
360        public boolean getBaseLinesVisible() {
361            return this.baseLinesVisible;
362        }
363    
364        /**
365         * Sets the base 'lines visible' flag and sends a
366         * {@link RendererChangeEvent} to all registered listeners.
367         *
368         * @param flag  the flag.
369         *
370         * @see #getBaseLinesVisible()
371         */
372        public void setBaseLinesVisible(boolean flag) {
373            this.baseLinesVisible = flag;
374            fireChangeEvent();
375        }
376    
377        // SHAPES VISIBLE
378    
379        /**
380         * Returns the flag used to control whether or not the shape for an item is
381         * visible.
382         *
383         * @param series  the series index (zero-based).
384         * @param item  the item index (zero-based).
385         *
386         * @return A boolean.
387         */
388        public boolean getItemShapeVisible(int series, int item) {
389            Boolean flag = this.shapesVisible;
390            if (flag == null) {
391                flag = getSeriesShapesVisible(series);
392            }
393            if (flag != null) {
394                return flag.booleanValue();
395            }
396            else {
397                return this.baseShapesVisible;
398            }
399        }
400    
401        /**
402         * Returns the flag that controls whether the shapes are visible for the
403         * items in ALL series.
404         *
405         * @return The flag (possibly <code>null</code>).
406         *
407         * @see #setShapesVisible(Boolean)
408         *
409         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
410         *     use the per-series and base (default) settings).
411         */
412        public Boolean getShapesVisible() {
413            return this.shapesVisible;
414        }
415    
416        /**
417         * Sets the 'shapes visible' for ALL series and sends a
418         * {@link RendererChangeEvent} to all registered listeners.
419         *
420         * @param visible  the flag (<code>null</code> permitted).
421         *
422         * @see #getShapesVisible()
423         *
424         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
425         *     use the per-series and base (default) settings).
426         */
427        public void setShapesVisible(Boolean visible) {
428            this.shapesVisible = visible;
429            fireChangeEvent();
430        }
431    
432        /**
433         * Sets the 'shapes visible' for ALL series and sends a
434         * {@link RendererChangeEvent} to all registered listeners.
435         *
436         * @param visible  the flag.
437         *
438         * @see #getShapesVisible()
439         *
440         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
441         *     use the per-series and base (default) settings).
442         */
443        public void setShapesVisible(boolean visible) {
444            setShapesVisible(BooleanUtilities.valueOf(visible));
445        }
446    
447        /**
448         * Returns the flag used to control whether or not the shapes for a series
449         * are visible.
450         *
451         * @param series  the series index (zero-based).
452         *
453         * @return A boolean.
454         *
455         * @see #setSeriesShapesVisible(int, Boolean)
456         */
457        public Boolean getSeriesShapesVisible(int series) {
458            return this.seriesShapesVisible.getBoolean(series);
459        }
460    
461        /**
462         * Sets the 'shapes visible' flag for a series and sends a
463         * {@link RendererChangeEvent} to all registered listeners.
464         *
465         * @param series  the series index (zero-based).
466         * @param visible  the flag.
467         *
468         * @see #getSeriesShapesVisible(int)
469         */
470        public void setSeriesShapesVisible(int series, boolean visible) {
471            setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
472        }
473    
474        /**
475         * Sets the 'shapes visible' flag for a series and sends a
476         * {@link RendererChangeEvent} to all registered listeners.
477         *
478         * @param series  the series index (zero-based).
479         * @param flag  the flag.
480         *
481         * @see #getSeriesShapesVisible(int)
482         */
483        public void setSeriesShapesVisible(int series, Boolean flag) {
484            this.seriesShapesVisible.setBoolean(series, flag);
485            fireChangeEvent();
486        }
487    
488        /**
489         * Returns the base 'shape visible' attribute.
490         *
491         * @return The base flag.
492         *
493         * @see #setBaseShapesVisible(boolean)
494         */
495        public boolean getBaseShapesVisible() {
496            return this.baseShapesVisible;
497        }
498    
499        /**
500         * Sets the base 'shapes visible' flag and sends a
501         * {@link RendererChangeEvent} to all registered listeners.
502         *
503         * @param flag  the flag.
504         *
505         * @see #getBaseShapesVisible()
506         */
507        public void setBaseShapesVisible(boolean flag) {
508            this.baseShapesVisible = flag;
509            fireChangeEvent();
510        }
511    
512        /**
513         * Returns <code>true</code> if outlines should be drawn for shapes, and
514         * <code>false</code> otherwise.
515         *
516         * @return A boolean.
517         *
518         * @see #setDrawOutlines(boolean)
519         */
520        public boolean getDrawOutlines() {
521            return this.drawOutlines;
522        }
523    
524        /**
525         * Sets the flag that controls whether outlines are drawn for
526         * shapes, and sends a {@link RendererChangeEvent} to all registered
527         * listeners.
528         * <P>
529         * In some cases, shapes look better if they do NOT have an outline, but
530         * this flag allows you to set your own preference.
531         *
532         * @param flag  the flag.
533         *
534         * @see #getDrawOutlines()
535         */
536        public void setDrawOutlines(boolean flag) {
537            this.drawOutlines = flag;
538            fireChangeEvent();
539        }
540    
541        /**
542         * Returns the flag that controls whether the outline paint is used for
543         * shape outlines.  If not, the regular series paint is used.
544         *
545         * @return A boolean.
546         *
547         * @see #setUseOutlinePaint(boolean)
548         */
549        public boolean getUseOutlinePaint() {
550            return this.useOutlinePaint;
551        }
552    
553        /**
554         * Sets the flag that controls whether the outline paint is used for shape
555         * outlines, and sends a {@link RendererChangeEvent} to all registered
556         * listeners.
557         *
558         * @param use  the flag.
559         *
560         * @see #getUseOutlinePaint()
561         */
562        public void setUseOutlinePaint(boolean use) {
563            this.useOutlinePaint = use;
564            fireChangeEvent();
565        }
566    
567        // SHAPES FILLED
568    
569        /**
570         * Returns the flag used to control whether or not the shape for an item
571         * is filled. The default implementation passes control to the
572         * <code>getSeriesShapesFilled</code> method. You can override this method
573         * if you require different behaviour.
574         *
575         * @param series  the series index (zero-based).
576         * @param item  the item index (zero-based).
577         *
578         * @return A boolean.
579         */
580        public boolean getItemShapeFilled(int series, int item) {
581            return getSeriesShapesFilled(series);
582        }
583    
584        /**
585         * Returns the flag used to control whether or not the shapes for a series
586         * are filled.
587         *
588         * @param series  the series index (zero-based).
589         *
590         * @return A boolean.
591         */
592        public boolean getSeriesShapesFilled(int series) {
593    
594            // return the overall setting, if there is one...
595            if (this.shapesFilled != null) {
596                return this.shapesFilled.booleanValue();
597            }
598    
599            // otherwise look up the paint table
600            Boolean flag = this.seriesShapesFilled.getBoolean(series);
601            if (flag != null) {
602                return flag.booleanValue();
603            }
604            else {
605                return this.baseShapesFilled;
606            }
607    
608        }
609    
610        /**
611         * Returns the flag that controls whether or not shapes are filled for
612         * ALL series.
613         *
614         * @return A Boolean.
615         *
616         * @see #setShapesFilled(Boolean)
617         *
618         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
619         *     use the per-series and base (default) settings).
620         */
621        public Boolean getShapesFilled() {
622            return this.shapesFilled;
623        }
624    
625        /**
626         * Sets the 'shapes filled' for ALL series and sends a
627         * {@link RendererChangeEvent} to all registered listeners.
628         *
629         * @param filled  the flag.
630         *
631         * @see #getShapesFilled()
632         *
633         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
634         *     use the per-series and base (default) settings).
635         */
636        public void setShapesFilled(boolean filled) {
637            if (filled) {
638                setShapesFilled(Boolean.TRUE);
639            }
640            else {
641                setShapesFilled(Boolean.FALSE);
642            }
643        }
644    
645        /**
646         * Sets the 'shapes filled' for ALL series and sends a
647         * {@link RendererChangeEvent} to all registered listeners.
648         *
649         * @param filled  the flag (<code>null</code> permitted).
650         *
651         * @see #getShapesFilled()
652         *
653         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
654         *     use the per-series and base (default) settings).
655         */
656        public void setShapesFilled(Boolean filled) {
657            this.shapesFilled = filled;
658            fireChangeEvent();
659        }
660    
661        /**
662         * Sets the 'shapes filled' flag for a series and sends a
663         * {@link RendererChangeEvent} to all registered listeners.
664         *
665         * @param series  the series index (zero-based).
666         * @param filled  the flag.
667         *
668         * @see #getSeriesShapesFilled(int)
669         */
670        public void setSeriesShapesFilled(int series, Boolean filled) {
671            this.seriesShapesFilled.setBoolean(series, filled);
672            fireChangeEvent();
673        }
674    
675        /**
676         * Sets the 'shapes filled' flag for a series and sends a
677         * {@link RendererChangeEvent} to all registered listeners.
678         *
679         * @param series  the series index (zero-based).
680         * @param filled  the flag.
681         *
682         * @see #getSeriesShapesFilled(int)
683         */
684        public void setSeriesShapesFilled(int series, boolean filled) {
685            // delegate
686            setSeriesShapesFilled(series, BooleanUtilities.valueOf(filled));
687        }
688    
689        /**
690         * Returns the base 'shape filled' attribute.
691         *
692         * @return The base flag.
693         *
694         * @see #setBaseShapesFilled(boolean)
695         */
696        public boolean getBaseShapesFilled() {
697            return this.baseShapesFilled;
698        }
699    
700        /**
701         * Sets the base 'shapes filled' flag and sends a
702         * {@link RendererChangeEvent} to all registered listeners.
703         *
704         * @param flag  the flag.
705         *
706         * @see #getBaseShapesFilled()
707         */
708        public void setBaseShapesFilled(boolean flag) {
709            this.baseShapesFilled = flag;
710            fireChangeEvent();
711        }
712    
713        /**
714         * Returns <code>true</code> if the renderer should use the fill paint
715         * setting to fill shapes, and <code>false</code> if it should just
716         * use the regular paint.
717         *
718         * @return A boolean.
719         *
720         * @see #setUseFillPaint(boolean)
721         */
722        public boolean getUseFillPaint() {
723            return this.useFillPaint;
724        }
725    
726        /**
727         * Sets the flag that controls whether the fill paint is used to fill
728         * shapes, and sends a {@link RendererChangeEvent} to all
729         * registered listeners.
730         *
731         * @param flag  the flag.
732         *
733         * @see #getUseFillPaint()
734         */
735        public void setUseFillPaint(boolean flag) {
736            this.useFillPaint = flag;
737            fireChangeEvent();
738        }
739    
740        /**
741         * Returns the flag that controls whether or not the x-position for each
742         * data item is offset within the category according to the series.
743         *
744         * @return A boolean.
745         *
746         * @see #setUseSeriesOffset(boolean)
747         *
748         * @since 1.0.7
749         */
750        public boolean getUseSeriesOffset() {
751            return this.useSeriesOffset;
752        }
753    
754        /**
755         * Sets the flag that controls whether or not the x-position for each
756         * data item is offset within its category according to the series, and
757         * sends a {@link RendererChangeEvent} to all registered listeners.
758         *
759         * @param offset  the offset.
760         *
761         * @see #getUseSeriesOffset()
762         *
763         * @since 1.0.7
764         */
765        public void setUseSeriesOffset(boolean offset) {
766            this.useSeriesOffset = offset;
767            fireChangeEvent();
768        }
769    
770        /**
771         * Returns the item margin, which is the gap between items within a
772         * category (expressed as a percentage of the overall category width).
773         * This can be used to match the offset alignment with the bars drawn by
774         * a {@link BarRenderer}).
775         *
776         * @return The item margin.
777         *
778         * @see #setItemMargin(double)
779         * @see #getUseSeriesOffset()
780         *
781         * @since 1.0.7
782         */
783        public double getItemMargin() {
784            return this.itemMargin;
785        }
786    
787        /**
788         * Sets the item margin, which is the gap between items within a category
789         * (expressed as a percentage of the overall category width), and sends
790         * a {@link RendererChangeEvent} to all registered listeners.
791         *
792         * @param margin  the margin (0.0 <= margin < 1.0).
793         *
794         * @see #getItemMargin()
795         * @see #getUseSeriesOffset()
796         *
797         * @since 1.0.7
798         */
799        public void setItemMargin(double margin) {
800            if (margin < 0.0 || margin >= 1.0) {
801                throw new IllegalArgumentException("Requires 0.0 <= margin < 1.0.");
802            }
803            this.itemMargin = margin;
804            fireChangeEvent();
805        }
806    
807        /**
808         * Returns a legend item for a series.
809         *
810         * @param datasetIndex  the dataset index (zero-based).
811         * @param series  the series index (zero-based).
812         *
813         * @return The legend item.
814         */
815        public LegendItem getLegendItem(int datasetIndex, int series) {
816    
817            CategoryPlot cp = getPlot();
818            if (cp == null) {
819                return null;
820            }
821    
822            if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
823                CategoryDataset dataset = cp.getDataset(datasetIndex);
824                String label = getLegendItemLabelGenerator().generateLabel(
825                        dataset, series);
826                String description = label;
827                String toolTipText = null;
828                if (getLegendItemToolTipGenerator() != null) {
829                    toolTipText = getLegendItemToolTipGenerator().generateLabel(
830                            dataset, series);
831                }
832                String urlText = null;
833                if (getLegendItemURLGenerator() != null) {
834                    urlText = getLegendItemURLGenerator().generateLabel(
835                            dataset, series);
836                }
837                Shape shape = lookupSeriesShape(series);
838                Paint paint = lookupSeriesPaint(series);
839                Paint fillPaint = (this.useFillPaint
840                        ? getItemFillPaint(series, 0) : paint);
841                boolean shapeOutlineVisible = this.drawOutlines;
842                Paint outlinePaint = (this.useOutlinePaint
843                        ? getItemOutlinePaint(series, 0) : paint);
844                Stroke outlineStroke = lookupSeriesOutlineStroke(series);
845                boolean lineVisible = getItemLineVisible(series, 0);
846                boolean shapeVisible = getItemShapeVisible(series, 0);
847                LegendItem result = new LegendItem(label, description, toolTipText,
848                        urlText, shapeVisible, shape, getItemShapeFilled(series, 0),
849                        fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke,
850                        lineVisible, new Line2D.Double(-7.0, 0.0, 7.0, 0.0),
851                        getItemStroke(series, 0), getItemPaint(series, 0));
852                result.setDataset(dataset);
853                result.setDatasetIndex(datasetIndex);
854                result.setSeriesKey(dataset.getRowKey(series));
855                result.setSeriesIndex(series);
856                return result;
857            }
858            return null;
859    
860        }
861    
862        /**
863         * This renderer uses two passes to draw the data.
864         *
865         * @return The pass count (<code>2</code> for this renderer).
866         */
867        public int getPassCount() {
868            return 2;
869        }
870    
871        /**
872         * Draw a single data item.
873         *
874         * @param g2  the graphics device.
875         * @param state  the renderer state.
876         * @param dataArea  the area in which the data is drawn.
877         * @param plot  the plot.
878         * @param domainAxis  the domain axis.
879         * @param rangeAxis  the range axis.
880         * @param dataset  the dataset.
881         * @param row  the row index (zero-based).
882         * @param column  the column index (zero-based).
883         * @param pass  the pass index.
884         */
885        public void drawItem(Graphics2D g2, CategoryItemRendererState state,
886                Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
887                ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
888                int pass) {
889    
890            // do nothing if item is not visible
891            if (!getItemVisible(row, column)) {
892                return;
893            }
894    
895            // do nothing if both the line and shape are not visible
896            if (!getItemLineVisible(row, column)
897                    && !getItemShapeVisible(row, column)) {
898                return;
899            }
900    
901            // nothing is drawn for null...
902            Number v = dataset.getValue(row, column);
903            if (v == null) {
904                return;
905            }
906    
907            PlotOrientation orientation = plot.getOrientation();
908    
909            // current data point...
910            double x1;
911            if (this.useSeriesOffset) {
912                x1 = domainAxis.getCategorySeriesMiddle(dataset.getColumnKey(
913                        column), dataset.getRowKey(row), dataset, this.itemMargin,
914                        dataArea, plot.getDomainAxisEdge());
915            }
916            else {
917                x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
918                        dataArea, plot.getDomainAxisEdge());
919            }
920            double value = v.doubleValue();
921            double y1 = rangeAxis.valueToJava2D(value, dataArea,
922                    plot.getRangeAxisEdge());
923    
924            if (pass == 0 && getItemLineVisible(row, column)) {
925                if (column != 0) {
926                    Number previousValue = dataset.getValue(row, column - 1);
927                    if (previousValue != null) {
928                        // previous data point...
929                        double previous = previousValue.doubleValue();
930                        double x0;
931                        if (this.useSeriesOffset) {
932                            x0 = domainAxis.getCategorySeriesMiddle(
933                                    dataset.getColumnKey(column - 1),
934                                    dataset.getRowKey(row), dataset,
935                                    this.itemMargin, dataArea,
936                                    plot.getDomainAxisEdge());
937                        }
938                        else {
939                            x0 = domainAxis.getCategoryMiddle(column - 1,
940                                    getColumnCount(), dataArea,
941                                    plot.getDomainAxisEdge());
942                        }
943                        double y0 = rangeAxis.valueToJava2D(previous, dataArea,
944                                plot.getRangeAxisEdge());
945    
946                        Line2D line = null;
947                        if (orientation == PlotOrientation.HORIZONTAL) {
948                            line = new Line2D.Double(y0, x0, y1, x1);
949                        }
950                        else if (orientation == PlotOrientation.VERTICAL) {
951                            line = new Line2D.Double(x0, y0, x1, y1);
952                        }
953                        g2.setPaint(getItemPaint(row, column));
954                        g2.setStroke(getItemStroke(row, column));
955                        g2.draw(line);
956                    }
957                }
958            }
959    
960            if (pass == 1) {
961                Shape shape = getItemShape(row, column);
962                if (orientation == PlotOrientation.HORIZONTAL) {
963                    shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
964                }
965                else if (orientation == PlotOrientation.VERTICAL) {
966                    shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
967                }
968    
969                if (getItemShapeVisible(row, column)) {
970                    if (getItemShapeFilled(row, column)) {
971                        if (this.useFillPaint) {
972                            g2.setPaint(getItemFillPaint(row, column));
973                        }
974                        else {
975                            g2.setPaint(getItemPaint(row, column));
976                        }
977                        g2.fill(shape);
978                    }
979                    if (this.drawOutlines) {
980                        if (this.useOutlinePaint) {
981                            g2.setPaint(getItemOutlinePaint(row, column));
982                        }
983                        else {
984                            g2.setPaint(getItemPaint(row, column));
985                        }
986                        g2.setStroke(getItemOutlineStroke(row, column));
987                        g2.draw(shape);
988                    }
989                }
990    
991                // draw the item label if there is one...
992                if (isItemLabelVisible(row, column)) {
993                    if (orientation == PlotOrientation.HORIZONTAL) {
994                        drawItemLabel(g2, orientation, dataset, row, column, y1,
995                                x1, (value < 0.0));
996                    }
997                    else if (orientation == PlotOrientation.VERTICAL) {
998                        drawItemLabel(g2, orientation, dataset, row, column, x1,
999                                y1, (value < 0.0));
1000                    }
1001                }
1002    
1003                // add an item entity, if this information is being collected
1004                EntityCollection entities = state.getEntityCollection();
1005                if (entities != null) {
1006                    addItemEntity(entities, dataset, row, column, shape);
1007                }
1008            }
1009    
1010        }
1011    
1012        /**
1013         * Tests this renderer for equality with an arbitrary object.
1014         *
1015         * @param obj  the object (<code>null</code> permitted).
1016         *
1017         * @return A boolean.
1018         */
1019        public boolean equals(Object obj) {
1020    
1021            if (obj == this) {
1022                return true;
1023            }
1024            if (!(obj instanceof LineAndShapeRenderer)) {
1025                return false;
1026            }
1027    
1028            LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
1029            if (this.baseLinesVisible != that.baseLinesVisible) {
1030                return false;
1031            }
1032            if (!ObjectUtilities.equal(this.seriesLinesVisible,
1033                    that.seriesLinesVisible)) {
1034                return false;
1035            }
1036            if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
1037                return false;
1038            }
1039            if (this.baseShapesVisible != that.baseShapesVisible) {
1040                return false;
1041            }
1042            if (!ObjectUtilities.equal(this.seriesShapesVisible,
1043                    that.seriesShapesVisible)) {
1044                return false;
1045            }
1046            if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
1047                return false;
1048            }
1049            if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
1050                return false;
1051            }
1052            if (!ObjectUtilities.equal(this.seriesShapesFilled,
1053                    that.seriesShapesFilled)) {
1054                return false;
1055            }
1056            if (this.baseShapesFilled != that.baseShapesFilled) {
1057                return false;
1058            }
1059            if (this.useOutlinePaint != that.useOutlinePaint) {
1060                return false;
1061            }
1062            if (this.useSeriesOffset != that.useSeriesOffset) {
1063                return false;
1064            }
1065            if (this.itemMargin != that.itemMargin) {
1066                return false;
1067            }
1068            return super.equals(obj);
1069        }
1070    
1071        /**
1072         * Returns an independent copy of the renderer.
1073         *
1074         * @return A clone.
1075         *
1076         * @throws CloneNotSupportedException  should not happen.
1077         */
1078        public Object clone() throws CloneNotSupportedException {
1079            LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone();
1080            clone.seriesLinesVisible
1081                = (BooleanList) this.seriesLinesVisible.clone();
1082            clone.seriesShapesVisible
1083                = (BooleanList) this.seriesShapesVisible.clone();
1084            clone.seriesShapesFilled
1085                = (BooleanList) this.seriesShapesFilled.clone();
1086            return clone;
1087        }
1088    
1089    }