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