    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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     * AreaRenderer.java
    029     * -----------------
    030     * (C) Copyright 2002-2008, by Jon Iles and Contributors.
    031     *
    032     * Original Author:  Jon Iles;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *                   Christian W. Zuckschwerdt;
    035     *
    036     * Changes:
    037     * --------
    038     * 21-May-2002 : Version 1, contributed by John Iles (DG);
    039     * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
    040     * 11-Jun-2002 : Updated Javadoc comments (DG);
    041     * 25-Jun-2002 : Removed unnecessary imports (DG);
    042     * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    043     * 10-Oct-2002 : Added constructors and basic entity support (DG);
    044     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
    045     *               CategoryToolTipGenerator interface (DG);
    046     * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
    047     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
    048     *               for category spacing.  Renamed AreaCategoryItemRenderer
    049     *               --> AreaRenderer (DG);
    050     * 17-Jan-2003 : Moved plot classes into a separate package (DG);
    051     * 25-Mar-2003 : Implemented Serializable (DG);
    052     * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in
    053     *               drawItem() method (DG);
    054     * 12-May-2003 : Modified to take into account the plot orientation (DG);
    055     * 30-Jul-2003 : Modified entity constructor (CZ);
    056     * 13-Aug-2003 : Implemented Cloneable (DG);
    057     * 07-Oct-2003 : Added renderer state (DG);
    058     * 05-Nov-2004 : Modified drawItem() signature (DG);
    059     * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG);
    060     * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
    061     * ------------- JFREECHART 1.0.x ---------------------------------------------
    062     * 11-Oct-2006 : Fixed bug in equals() method (DG);
    063     * 30-Nov-2006 : Added checks for series visibility (DG);
    064     * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
    065     * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
    066     * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
    067     *
    068     */
    069
    070    package org.jfree.chart.renderer.category;
    071
    072    import java.awt.Graphics2D;
    073    import java.awt.Paint;
    074    import java.awt.Shape;
    075    import java.awt.Stroke;
    076    import java.awt.geom.GeneralPath;
    077    import java.awt.geom.Rectangle2D;
    078    import java.io.Serializable;
    079
    080    import org.jfree.chart.LegendItem;
    081    import org.jfree.chart.axis.CategoryAxis;
    082    import org.jfree.chart.axis.ValueAxis;
    083    import org.jfree.chart.entity.EntityCollection;
    084    import org.jfree.chart.event.RendererChangeEvent;
    085    import org.jfree.chart.plot.CategoryPlot;
    086    import org.jfree.chart.plot.PlotOrientation;
    087    import org.jfree.chart.renderer.AreaRendererEndType;
    088    import org.jfree.data.category.CategoryDataset;
    089    import org.jfree.ui.RectangleEdge;
    090    import org.jfree.util.PublicCloneable;
    091
    092    /**
    093     * A category item renderer that draws area charts.  You can use this renderer
    094     * with the {@link org.jfree.chart.plot.CategoryPlot} class.
    095     */
    096    public class AreaRenderer extends AbstractCategoryItemRenderer
    097            implements Cloneable, PublicCloneable, Serializable {
    098
    099        /** For serialization. */
    100        private static final long serialVersionUID = -4231878281385812757L;
    101
    102        /** A flag that controls how the ends of the areas are drawn. */
    103        private AreaRendererEndType endType;
    104
    105        /**
    106         * Creates a new renderer.
    107         */
    108        public AreaRenderer() {
    109            super();
    110            this.endType = AreaRendererEndType.TAPER;
    111        }
    112
    113        /**
    114         * Returns a token that controls how the renderer draws the end points.
    115         * The default value is {@link AreaRendererEndType#TAPER}.
    116         *
    117         * @return The end type (never <code>null</code>).
    118         *
    119         * @see #setEndType
    120         */
    121        public AreaRendererEndType getEndType() {
    122            return this.endType;
    123        }
    124
    125        /**
    126         * Sets a token that controls how the renderer draws the end points, and
    127         * sends a {@link RendererChangeEvent} to all registered listeners.
    128         *
    129         * @param type  the end type (<code>null</code> not permitted).
    130         *
    131         * @see #getEndType()
    132         */
    133        public void setEndType(AreaRendererEndType type) {
    134            if (type == null) {
    135                throw new IllegalArgumentException("Null 'type' argument.");
    136            }
    137            this.endType = type;
    138            fireChangeEvent();
    139        }
    140
    141        /**
    142         * Returns a legend item for a series.
    143         *
    144         * @param datasetIndex  the dataset index (zero-based).
    145         * @param series  the series index (zero-based).
    146         *
    147         * @return The legend item.
    148         */
    149        public LegendItem getLegendItem(int datasetIndex, int series) {
    150
    151            // if there is no plot, there is no dataset to access...
    152            CategoryPlot cp = getPlot();
    153            if (cp == null) {
    154                return null;
    155            }
    156
    157            // check that a legend item needs to be displayed...
    158            if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
    159                return null;
    160            }
    161
    162            CategoryDataset dataset = cp.getDataset(datasetIndex);
    163            String label = getLegendItemLabelGenerator().generateLabel(dataset,
    164                    series);
    165            String description = label;
    166            String toolTipText = null;
    167            if (getLegendItemToolTipGenerator() != null) {
    168                toolTipText = getLegendItemToolTipGenerator().generateLabel(
    169                        dataset, series);
    170            }
    171            String urlText = null;
    172            if (getLegendItemURLGenerator() != null) {
    173                urlText = getLegendItemURLGenerator().generateLabel(dataset,
    174                        series);
    175            }
    176            Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
    177            Paint paint = lookupSeriesPaint(series);
    178            Paint outlinePaint = lookupSeriesOutlinePaint(series);
    179            Stroke outlineStroke = lookupSeriesOutlineStroke(series);
    180
    181            LegendItem result = new LegendItem(label, description, toolTipText,
    182                    urlText, shape, paint, outlineStroke, outlinePaint);
    183            result.setDataset(dataset);
    184            result.setDatasetIndex(datasetIndex);
    185            result.setSeriesKey(dataset.getRowKey(series));
    186            result.setSeriesIndex(series);
    187            return result;
    188
    189        }
    190
    191        /**
    192         * Draw a single data item.
    193         *
    194         * @param g2  the graphics device.
    195         * @param state  the renderer state.
    196         * @param dataArea  the data plot area.
    197         * @param plot  the plot.
    198         * @param domainAxis  the domain axis.
    199         * @param rangeAxis  the range axis.
    200         * @param dataset  the dataset.
    201         * @param row  the row index (zero-based).
    202         * @param column  the column index (zero-based).
    203         * @param pass  the pass index.
    204         */
    205        public void drawItem(Graphics2D g2,
    206                             CategoryItemRendererState state,
    207                             Rectangle2D dataArea,
    208                             CategoryPlot plot,
    209                             CategoryAxis domainAxis,
    210                             ValueAxis rangeAxis,
    211                             CategoryDataset dataset,
    212                             int row,
    213                             int column,
    214                             int pass) {
    215
    216            // do nothing if item is not visible
    217            if (!getItemVisible(row, column)) {
    218                return;
    219            }
    220
    221            // plot non-null values only...
    222            Number value = dataset.getValue(row, column);
    223            if (value != null) {
    224                PlotOrientation orientation = plot.getOrientation();
    225                RectangleEdge axisEdge = plot.getDomainAxisEdge();
    226                int count = dataset.getColumnCount();
    227                float x0 = (float) domainAxis.getCategoryStart(column, count,
    228                        dataArea, axisEdge);
    229                float x1 = (float) domainAxis.getCategoryMiddle(column, count,
    230                        dataArea, axisEdge);
    231                float x2 = (float) domainAxis.getCategoryEnd(column, count,
    232                        dataArea, axisEdge);
    233
    234                x0 = Math.round(x0);
    235                x1 = Math.round(x1);
    236                x2 = Math.round(x2);
    237
    238                if (this.endType == AreaRendererEndType.TRUNCATE) {
    239                    if (column == 0) {
    240                        x0 = x1;
    241                    }
    242                    else if (column == getColumnCount() - 1) {
    243                        x2 = x1;
    244                    }
    245                }
    246
    247                double yy1 = value.doubleValue();
    248
    249                double yy0 = 0.0;
    250                if (column > 0) {
    251                    Number n0 = dataset.getValue(row, column - 1);
    252                    if (n0 != null) {
    253                        yy0 = (n0.doubleValue() + yy1) / 2.0;
    254                    }
    255                }
    256
    257                double yy2 = 0.0;
    258                if (column < dataset.getColumnCount() - 1) {
    259                    Number n2 = dataset.getValue(row, column + 1);
    260                    if (n2 != null) {
    261                        yy2 = (n2.doubleValue() + yy1) / 2.0;
    262                    }
    263                }
    264
    265                RectangleEdge edge = plot.getRangeAxisEdge();
    266                float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge);
    267                float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge);
    268                float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge);
    269                float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge);
    270
    271                g2.setPaint(getItemPaint(row, column));
    272                g2.setStroke(getItemStroke(row, column));
    273
    274                GeneralPath area = new GeneralPath();
    275
    276                if (orientation == PlotOrientation.VERTICAL) {
    277                    area.moveTo(x0, yz);
    278                    area.lineTo(x0, y0);
    279                    area.lineTo(x1, y1);
    280                    area.lineTo(x2, y2);
    281                    area.lineTo(x2, yz);
    282                }
    283                else if (orientation == PlotOrientation.HORIZONTAL) {
    284                    area.moveTo(yz, x0);
    285                    area.lineTo(y0, x0);
    286                    area.lineTo(y1, x1);
    287                    area.lineTo(y2, x2);
    288                    area.lineTo(yz, x2);
    289                }
    290                area.closePath();
    291
    292                g2.setPaint(getItemPaint(row, column));
    293                g2.fill(area);
    294
    295                // draw the item labels if there are any...
    296                if (isItemLabelVisible(row, column)) {
    297                    drawItemLabel(g2, orientation, dataset, row, column, x1, y1,
    298                            (value.doubleValue() < 0.0));
    299                }
    300
    301                // add an item entity, if this information is being collected
    302                EntityCollection entities = state.getEntityCollection();
    303                if (entities != null) {
    304                    addItemEntity(entities, dataset, row, column, area);
    305                }
    306            }
    307
    308        }
    309
    310        /**
    311         * Tests this instance for equality with an arbitrary object.
    312         *
    313         * @param obj  the object to test (<code>null</code> permitted).
    314         *
    315         * @return A boolean.
    316         */
    317        public boolean equals(Object obj) {
    318            if (obj == this) {
    319                return true;
    320            }
    321            if (!(obj instanceof AreaRenderer)) {
    322                return false;
    323            }
    324            AreaRenderer that = (AreaRenderer) obj;
    325            if (!this.endType.equals(that.endType)) {
    326                return false;
    327            }
    328            return super.equals(obj);
    329        }
    330
    331        /**
    332         * Returns an independent copy of the renderer.
    333         *
    334         * @return A clone.
    335         *
    336         * @throws CloneNotSupportedException  should not happen.
    337         */
    338        public Object clone() throws CloneNotSupportedException {
    339            return super.clone();
    340        }
    341
    342    }