    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    }