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