    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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-2005, 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.5 2005/11/28 12:06:35 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     *
    083     */
    084
    085    package org.jfree.chart.renderer.category;
    086
    087    import java.awt.Graphics2D;
    088    import java.awt.Paint;
    089    import java.awt.Shape;
    090    import java.awt.Stroke;
    091    import java.awt.geom.Line2D;
    092    import java.awt.geom.Rectangle2D;
    093    import java.io.Serializable;
    094
    095    import org.jfree.chart.LegendItem;
    096    import org.jfree.chart.axis.CategoryAxis;
    097    import org.jfree.chart.axis.ValueAxis;
    098    import org.jfree.chart.entity.EntityCollection;
    099    import org.jfree.chart.event.RendererChangeEvent;
    100    import org.jfree.chart.plot.CategoryPlot;
    101    import org.jfree.chart.plot.PlotOrientation;
    102    import org.jfree.data.category.CategoryDataset;
    103    import org.jfree.util.BooleanList;
    104    import org.jfree.util.BooleanUtilities;
    105    import org.jfree.util.ObjectUtilities;
    106    import org.jfree.util.PublicCloneable;
    107    import org.jfree.util.ShapeUtilities;
    108
    109    /**
    110     * A renderer that draws shapes for each data item, and lines between data
    111     * items (for use with the {@link CategoryPlot} class).
    112     */
    113    public class LineAndShapeRenderer extends AbstractCategoryItemRenderer
    114                                      implements Cloneable, PublicCloneable,
    115                                                 Serializable {
    116
    117        /** For serialization. */
    118        private static final long serialVersionUID = -197749519869226398L;
    119
    120        /** A flag that controls whether or not lines are visible for ALL series. */
    121        private Boolean linesVisible;
    122
    123        /**
    124         * A table of flags that control (per series) whether or not lines are
    125         * visible.
    126         */
    127        private BooleanList seriesLinesVisible;
    128
    129        /**
    130         * A flag indicating whether or not lines are drawn between non-null
    131         * points.
    132         */
    133        private boolean baseLinesVisible;
    134
    135        /**
    136         * A flag that controls whether or not shapes are visible for ALL series.
    137         */
    138        private Boolean shapesVisible;
    139
    140        /**
    141         * A table of flags that control (per series) whether or not shapes are
    142         * visible.
    143         */
    144        private BooleanList seriesShapesVisible;
    145
    146        /** The default value returned by the getShapeVisible() method. */
    147        private boolean baseShapesVisible;
    148
    149        /** A flag that controls whether or not shapes are filled for ALL series. */
    150        private Boolean shapesFilled;
    151
    152        /**
    153         * A table of flags that control (per series) whether or not shapes are
    154         * filled.
    155         */
    156        private BooleanList seriesShapesFilled;
    157
    158        /** The default value returned by the getShapeFilled() method. */
    159        private boolean baseShapesFilled;
    160
    161        /**
    162         * A flag that controls whether the fill paint is used for filling
    163         * shapes.
    164         */
    165        private boolean useFillPaint;
    166
    167        /** A flag that controls whether outlines are drawn for shapes. */
    168        private boolean drawOutlines;
    169
    170        /**
    171         * A flag that controls whether the outline paint is used for drawing shape
    172         * outlines - if not, the regular series paint is used.
    173         */
    174        private boolean useOutlinePaint;
    175
    176        /**
    177         * Creates a renderer with both lines and shapes visible by default.
    178         */
    179        public LineAndShapeRenderer() {
    180            this(true, true);
    181        }
    182
    183        /**
    184         * Creates a new renderer with lines and/or shapes visible.
    185         *
    186         * @param lines  draw lines?
    187         * @param shapes  draw shapes?
    188         */
    189        public LineAndShapeRenderer(boolean lines, boolean shapes) {
    190            super();
    191            this.linesVisible = null;
    192            this.seriesLinesVisible = new BooleanList();
    193            this.baseLinesVisible = lines;
    194            this.shapesVisible = null;
    195            this.seriesShapesVisible = new BooleanList();
    196            this.baseShapesVisible = shapes;
    197            this.shapesFilled = null;
    198            this.seriesShapesFilled = new BooleanList();
    199            this.baseShapesFilled = true;
    200            this.useFillPaint = false;
    201            this.drawOutlines = true;
    202            this.useOutlinePaint = false;
    203        }
    204
    205        // LINES VISIBLE
    206
    207        /**
    208         * Returns the flag used to control whether or not the line for an item is
    209         * visible.
    210         *
    211         * @param series  the series index (zero-based).
    212         * @param item  the item index (zero-based).
    213         *
    214         * @return A boolean.
    215         */
    216        public boolean getItemLineVisible(int series, int item) {
    217            Boolean flag = this.linesVisible;
    218            if (flag == null) {
    219                flag = getSeriesLinesVisible(series);
    220            }
    221            if (flag != null) {
    222                return flag.booleanValue();
    223            }
    224            else {
    225                return this.baseLinesVisible;
    226            }
    227        }
    228
    229        /**
    230         * Returns a flag that controls whether or not lines are drawn for ALL
    231         * series.  If this flag is <code>null</code>, then the "per series"
    232         * settings will apply.
    233         *
    234         * @return A flag (possibly <code>null</code>).
    235         */
    236        public Boolean getLinesVisible() {
    237            return this.linesVisible;
    238        }
    239
    240        /**
    241         * Sets a flag that controls whether or not lines are drawn between the
    242         * items in ALL series, and sends a {@link RendererChangeEvent} to all
    243         * registered listeners.  You need to set this to <code>null</code> if you
    244         * want the "per series" settings to apply.
    245         *
    246         * @param visible  the flag (<code>null</code> permitted).
    247         */
    248        public void setLinesVisible(Boolean visible) {
    249            this.linesVisible = visible;
    250            notifyListeners(new RendererChangeEvent(this));
    251        }
    252
    253        /**
    254         * Sets a flag that controls whether or not lines are drawn between the
    255         * items in ALL series, and sends a {@link RendererChangeEvent} to all
    256         * registered listeners.
    257         *
    258         * @param visible  the flag.
    259         */
    260        public void setLinesVisible(boolean visible) {
    261            setLinesVisible(BooleanUtilities.valueOf(visible));
    262        }
    263
    264        /**
    265         * Returns the flag used to control whether or not the lines for a series
    266         * are visible.
    267         *
    268         * @param series  the series index (zero-based).
    269         *
    270         * @return The flag (possibly <code>null</code>).
    271         */
    272        public Boolean getSeriesLinesVisible(int series) {
    273            return this.seriesLinesVisible.getBoolean(series);
    274        }
    275
    276        /**
    277         * Sets the 'lines visible' flag for a series.
    278         *
    279         * @param series  the series index (zero-based).
    280         * @param flag  the flag (<code>null</code> permitted).
    281         */
    282        public void setSeriesLinesVisible(int series, Boolean flag) {
    283            this.seriesLinesVisible.setBoolean(series, flag);
    284            notifyListeners(new RendererChangeEvent(this));
    285        }
    286
    287        /**
    288         * Sets the 'lines visible' flag for a series.
    289         *
    290         * @param series  the series index (zero-based).
    291         * @param visible  the flag.
    292         */
    293        public void setSeriesLinesVisible(int series, boolean visible) {
    294            setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
    295        }
    296
    297        /**
    298         * Returns the base 'lines visible' attribute.
    299         *
    300         * @return The base flag.
    301         */
    302        public boolean getBaseLinesVisible() {
    303            return this.baseLinesVisible;
    304        }
    305
    306        /**
    307         * Sets the base 'lines visible' flag.
    308         *
    309         * @param flag  the flag.
    310         */
    311        public void setBaseLinesVisible(boolean flag) {
    312            this.baseLinesVisible = flag;
    313            notifyListeners(new RendererChangeEvent(this));
    314        }
    315
    316        // SHAPES VISIBLE
    317
    318        /**
    319         * Returns the flag used to control whether or not the shape for an item is
    320         * visible.
    321         *
    322         * @param series  the series index (zero-based).
    323         * @param item  the item index (zero-based).
    324         *
    325         * @return A boolean.
    326         */
    327        public boolean getItemShapeVisible(int series, int item) {
    328            Boolean flag = this.shapesVisible;
    329            if (flag == null) {
    330                flag = getSeriesShapesVisible(series);
    331            }
    332            if (flag != null) {
    333                return flag.booleanValue();
    334            }
    335            else {
    336                return this.baseShapesVisible;
    337            }
    338        }
    339
    340        /**
    341         * Returns the flag that controls whether the shapes are visible for the
    342         * items in ALL series.
    343         *
    344         * @return The flag (possibly <code>null</code>).
    345         */
    346        public Boolean getShapesVisible() {
    347            return this.shapesVisible;
    348        }
    349
    350        /**
    351         * Sets the 'shapes visible' for ALL series and sends a
    352         * {@link RendererChangeEvent} to all registered listeners.
    353         *
    354         * @param visible  the flag (<code>null</code> permitted).
    355         */
    356        public void setShapesVisible(Boolean visible) {
    357            this.shapesVisible = visible;
    358            notifyListeners(new RendererChangeEvent(this));
    359        }
    360
    361        /**
    362         * Sets the 'shapes visible' for ALL series and sends a
    363         * {@link RendererChangeEvent} to all registered listeners.
    364         *
    365         * @param visible  the flag.
    366         */
    367        public void setShapesVisible(boolean visible) {
    368            setShapesVisible(BooleanUtilities.valueOf(visible));
    369        }
    370
    371        /**
    372         * Returns the flag used to control whether or not the shapes for a series
    373         * are visible.
    374         *
    375         * @param series  the series index (zero-based).
    376         *
    377         * @return A boolean.
    378         */
    379        public Boolean getSeriesShapesVisible(int series) {
    380            return this.seriesShapesVisible.getBoolean(series);
    381        }
    382
    383        /**
    384         * Sets the 'shapes visible' flag for a series and sends a
    385         * {@link RendererChangeEvent} to all registered listeners.
    386         *
    387         * @param series  the series index (zero-based).
    388         * @param visible  the flag.
    389         */
    390        public void setSeriesShapesVisible(int series, boolean visible) {
    391            setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
    392        }
    393
    394        /**
    395         * Sets the 'shapes visible' flag for a series and sends a
    396         * {@link RendererChangeEvent} to all registered listeners.
    397         *
    398         * @param series  the series index (zero-based).
    399         * @param flag  the flag.
    400         */
    401        public void setSeriesShapesVisible(int series, Boolean flag) {
    402            this.seriesShapesVisible.setBoolean(series, flag);
    403            notifyListeners(new RendererChangeEvent(this));
    404        }
    405
    406        /**
    407         * Returns the base 'shape visible' attribute.
    408         *
    409         * @return The base flag.
    410         */
    411        public boolean getBaseShapesVisible() {
    412            return this.baseShapesVisible;
    413        }
    414
    415        /**
    416         * Sets the base 'shapes visible' flag.
    417         *
    418         * @param flag  the flag.
    419         */
    420        public void setBaseShapesVisible(boolean flag) {
    421            this.baseShapesVisible = flag;
    422            notifyListeners(new RendererChangeEvent(this));
    423        }
    424
    425        /**
    426         * Returns <code>true</code> if outlines should be drawn for shapes, and
    427         * <code>false</code> otherwise.
    428         *
    429         * @return A boolean.
    430         */
    431        public boolean getDrawOutlines() {
    432            return this.drawOutlines;
    433        }
    434
    435        /**
    436         * Sets the flag that controls whether outlines are drawn for
    437         * shapes, and sends a {@link RendererChangeEvent} to all registered
    438         * listeners.
    439         * <P>
    440         * In some cases, shapes look better if they do NOT have an outline, but
    441         * this flag allows you to set your own preference.
    442         *
    443         * @param flag  the flag.
    444         */
    445        public void setDrawOutlines(boolean flag) {
    446            this.drawOutlines = flag;
    447            notifyListeners(new RendererChangeEvent(this));
    448        }
    449
    450        /**
    451         * Returns the flag that controls whether the outline paint is used for
    452         * shape outlines.  If not, the regular series paint is used.
    453         *
    454         * @return A boolean.
    455         */
    456        public boolean getUseOutlinePaint() {
    457            return this.useOutlinePaint;
    458        }
    459
    460        /**
    461         * Sets the flag that controls whether the outline paint is used for shape
    462         * outlines.
    463         *
    464         * @param use  the flag.
    465         */
    466        public void setUseOutlinePaint(boolean use) {
    467            this.useOutlinePaint = use;
    468        }
    469
    470        // SHAPES FILLED
    471
    472        /**
    473         * Returns the flag used to control whether or not the shape for an item
    474         * is filled. The default implementation passes control to the
    475         * <code>getSeriesShapesFilled</code> method. You can override this method
    476         * if you require different behaviour.
    477         *
    478         * @param series  the series index (zero-based).
    479         * @param item  the item index (zero-based).
    480         *
    481         * @return A boolean.
    482         */
    483        public boolean getItemShapeFilled(int series, int item) {
    484            return getSeriesShapesFilled(series);
    485        }
    486
    487        /**
    488         * Returns the flag used to control whether or not the shapes for a series
    489         * are filled.
    490         *
    491         * @param series  the series index (zero-based).
    492         *
    493         * @return A boolean.
    494         */
    495        public boolean getSeriesShapesFilled(int series) {
    496
    497            // return the overall setting, if there is one...
    498            if (this.shapesFilled != null) {
    499                return this.shapesFilled.booleanValue();
    500            }
    501
    502            // otherwise look up the paint table
    503            Boolean flag = this.seriesShapesFilled.getBoolean(series);
    504            if (flag != null) {
    505                return flag.booleanValue();
    506            }
    507            else {
    508                return this.baseShapesFilled;
    509            }
    510
    511        }
    512
    513        /**
    514         * Returns the flag that controls whether or not shapes are filled for
    515         * ALL series.
    516         *
    517         * @return A Boolean.
    518         */
    519        public Boolean getShapesFilled() {
    520            return this.shapesFilled;
    521        }
    522
    523        /**
    524         * Sets the 'shapes filled' for ALL series.
    525         *
    526         * @param filled  the flag.
    527         */
    528        public void setShapesFilled(boolean filled) {
    529            if (filled) {
    530                setShapesFilled(Boolean.TRUE);
    531            }
    532            else {
    533                setShapesFilled(Boolean.FALSE);
    534            }
    535        }
    536
    537        /**
    538         * Sets the 'shapes filled' for ALL series.
    539         *
    540         * @param filled  the flag (<code>null</code> permitted).
    541         */
    542        public void setShapesFilled(Boolean filled) {
    543            this.shapesFilled = filled;
    544        }
    545
    546        /**
    547         * Sets the 'shapes filled' flag for a series.
    548         *
    549         * @param series  the series index (zero-based).
    550         * @param filled  the flag.
    551         */
    552        public void setSeriesShapesFilled(int series, Boolean filled) {
    553            this.seriesShapesFilled.setBoolean(series, filled);
    554        }
    555
    556        /**
    557         * Sets the 'shapes filled' flag for a series.
    558         *
    559         * @param series  the series index (zero-based).
    560         * @param filled  the flag.
    561         */
    562        public void setSeriesShapesFilled(int series, boolean filled) {
    563            this.seriesShapesFilled.setBoolean(
    564                series, BooleanUtilities.valueOf(filled)
    565            );
    566        }
    567
    568        /**
    569         * Returns the base 'shape filled' attribute.
    570         *
    571         * @return The base flag.
    572         */
    573        public boolean getBaseShapesFilled() {
    574            return this.baseShapesFilled;
    575        }
    576
    577        /**
    578         * Sets the base 'shapes filled' flag.
    579         *
    580         * @param flag  the flag.
    581         */
    582        public void setBaseShapesFilled(boolean flag) {
    583            this.baseShapesFilled = flag;
    584        }
    585
    586        /**
    587         * Returns <code>true</code> if the renderer should use the fill paint
    588         * setting to fill shapes, and <code>false</code> if it should just
    589         * use the regular paint.
    590         *
    591         * @return A boolean.
    592         */
    593        public boolean getUseFillPaint() {
    594            return this.useFillPaint;
    595        }
    596
    597        /**
    598         * Sets the flag that controls whether the fill paint is used to fill
    599         * shapes, and sends a {@link RendererChangeEvent} to all
    600         * registered listeners.
    601         *
    602         * @param flag  the flag.
    603         */
    604        public void setUseFillPaint(boolean flag) {
    605            this.useFillPaint = flag;
    606            notifyListeners(new RendererChangeEvent(this));
    607        }
    608
    609        /**
    610         * Returns a legend item for a series.
    611         *
    612         * @param datasetIndex  the dataset index (zero-based).
    613         * @param series  the series index (zero-based).
    614         *
    615         * @return The legend item.
    616         */
    617        public LegendItem getLegendItem(int datasetIndex, int series) {
    618
    619            CategoryPlot cp = getPlot();
    620            if (cp == null) {
    621                return null;
    622            }
    623
    624            if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
    625                CategoryDataset dataset;
    626                dataset = cp.getDataset(datasetIndex);
    627                String label = getLegendItemLabelGenerator().generateLabel(
    628                    dataset, series
    629                );
    630                String description = label;
    631                String toolTipText = null;
    632                if (getLegendItemToolTipGenerator() != null) {
    633                    toolTipText = getLegendItemToolTipGenerator().generateLabel(
    634                        dataset, series
    635                    );
    636                }
    637                String urlText = null;
    638                if (getLegendItemURLGenerator() != null) {
    639                    urlText = getLegendItemURLGenerator().generateLabel(
    640                        dataset, series
    641                    );
    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,
    687                             CategoryItemRendererState state,
    688                             Rectangle2D dataArea,
    689                             CategoryPlot plot,
    690                             CategoryAxis domainAxis,
    691                             ValueAxis rangeAxis,
    692                             CategoryDataset dataset,
    693                             int row,
    694                             int column,
    695                             int pass) {
    696
    697            // do nothing if item is not visible
    698            if (!getItemVisible(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(
    712                column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
    713            );
    714            double value = v.doubleValue();
    715            double y1 = rangeAxis.valueToJava2D(
    716                value, dataArea, plot.getRangeAxisEdge()
    717            );
    718
    719            if (pass == 0 && getItemLineVisible(row, column)) {
    720                if (column != 0) {
    721                    Number previousValue = dataset.getValue(row, column - 1);
    722                    if (previousValue != null) {
    723                        // previous data point...
    724                        double previous = previousValue.doubleValue();
    725                        double x0 = domainAxis.getCategoryMiddle(
    726                            column - 1, getColumnCount(), dataArea,
    727                            plot.getDomainAxisEdge()
    728                        );
    729                        double y0 = rangeAxis.valueToJava2D(
    730                            previous, dataArea, plot.getRangeAxisEdge()
    731                        );
    732
    733                        Line2D line = null;
    734                        if (orientation == PlotOrientation.HORIZONTAL) {
    735                            line = new Line2D.Double(y0, x0, y1, x1);
    736                        }
    737                        else if (orientation == PlotOrientation.VERTICAL) {
    738                            line = new Line2D.Double(x0, y0, x1, y1);
    739                        }
    740                        g2.setPaint(getItemPaint(row, column));
    741                        g2.setStroke(getItemStroke(row, column));
    742                        g2.draw(line);
    743                    }
    744                }
    745            }
    746
    747            if (pass == 1) {
    748                Shape shape = getItemShape(row, column);
    749                if (orientation == PlotOrientation.HORIZONTAL) {
    750                    shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
    751                }
    752                else if (orientation == PlotOrientation.VERTICAL) {
    753                    shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
    754                }
    755
    756                if (getItemShapeVisible(row, column)) {
    757                    if (getItemShapeFilled(row, column)) {
    758                        if (this.useFillPaint) {
    759                            g2.setPaint(getItemFillPaint(row, column));
    760                        }
    761                        else {
    762                            g2.setPaint(getItemPaint(row, column));
    763                        }
    764                        g2.fill(shape);
    765                    }
    766                    if (this.drawOutlines) {
    767                        if (this.useOutlinePaint) {
    768                            g2.setPaint(getItemOutlinePaint(row, column));
    769                        }
    770                        else {
    771                            g2.setPaint(getItemPaint(row, column));
    772                        }
    773                        g2.setStroke(getItemOutlineStroke(row, column));
    774                        g2.draw(shape);
    775                    }
    776                }
    777
    778                // draw the item label if there is one...
    779                if (isItemLabelVisible(row, column)) {
    780                    if (orientation == PlotOrientation.HORIZONTAL) {
    781                        drawItemLabel(
    782                            g2, orientation, dataset, row, column, y1, x1,
    783                            (value < 0.0)
    784                        );
    785                    }
    786                    else if (orientation == PlotOrientation.VERTICAL) {
    787                        drawItemLabel(
    788                            g2, orientation, dataset, row, column, x1, y1,
    789                            (value < 0.0)
    790                        );
    791                    }
    792                }
    793
    794                // add an item entity, if this information is being collected
    795                EntityCollection entities = state.getEntityCollection();
    796                if (entities != null) {
    797                    addItemEntity(entities, dataset, row, column, shape);
    798                }
    799
    800            }
    801
    802        }
    803
    804        /**
    805         * Tests this renderer for equality with an arbitrary object.
    806         *
    807         * @param obj  the object (<code>null</code> permitted).
    808         *
    809         * @return A boolean.
    810         */
    811        public boolean equals(Object obj) {
    812
    813            if (obj == this) {
    814                return true;
    815            }
    816            if (!(obj instanceof LineAndShapeRenderer)) {
    817                return false;
    818            }
    819
    820            LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
    821            if (this.baseLinesVisible != that.baseLinesVisible) {
    822                return false;
    823            }
    824            if (!ObjectUtilities.equal(this.seriesLinesVisible,
    825                    that.seriesLinesVisible)) {
    826                return false;
    827            }
    828            if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
    829                return false;
    830            }
    831            if (this.baseShapesVisible != that.baseShapesVisible) {
    832                return false;
    833            }
    834            if (!ObjectUtilities.equal(this.seriesShapesVisible,
    835                    that.seriesShapesVisible)) {
    836                return false;
    837            }
    838            if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
    839                return false;
    840            }
    841            if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
    842                return false;
    843            }
    844            if (!ObjectUtilities.equal(
    845                this.seriesShapesFilled, that.seriesShapesFilled)
    846            ) {
    847                return false;
    848            }
    849            if (this.baseShapesFilled != that.baseShapesFilled) {
    850                return false;
    851            }
    852            if (this.useOutlinePaint != that.useOutlinePaint) {
    853                return false;
    854            }
    855            if (!super.equals(obj)) {
    856                return false;
    857            }
    858            return true;
    859        }
    860
    861        /**
    862         * Returns an independent copy of the renderer.
    863         *
    864         * @return A clone.
    865         *
    866         * @throws CloneNotSupportedException  should not happen.
    867         */
    868        public Object clone() throws CloneNotSupportedException {
    869            LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone();
    870            clone.seriesLinesVisible
    871                = (BooleanList) this.seriesLinesVisible.clone();
    872            clone.seriesShapesVisible
    873                = (BooleanList) this.seriesLinesVisible.clone();
    874            clone.seriesShapesFilled
    875                = (BooleanList) this.seriesShapesFilled.clone();
    876            return clone;
    877        }
    878
    879    }