    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     * StackedXYBarRenderer.java
    029     * -------------------------
    030     * (C) Copyright 2004, 2005, by Andreas Schroeder and Contributors.
    031     *
    032     * Original Author:  Andreas Schroeder;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * $Id: StackedXYBarRenderer.java,v 1.10.2.1 2005/10/25 20:56:21 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 01-Apr-2004 : Version 1 (AS);
    040     * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
    041     *               getYValue() (DG);
    042     * 15-Aug-2004 : Added drawBarOutline to control draw/don't-draw bar
    043     *               outlines (BN);
    044     * 10-Sep-2004 : drawBarOutline attribute is now inherited from XYBarRenderer
    045     *               and double primitives are retrieved from the dataset rather
    046     *               than Number objects (DG);
    047     * 07-Jan-2005 : Updated for method name change in DatasetUtilities (DG);
    048     * 25-Jan-2005 : Modified to handle negative values correctly (DG);
    049     *
    050     */
    051
    052    package org.jfree.chart.renderer.xy;
    053
    054    import java.awt.Graphics2D;
    055    import java.awt.geom.Rectangle2D;
    056    import java.io.Serializable;
    057
    058    import org.jfree.chart.axis.ValueAxis;
    059    import org.jfree.chart.entity.EntityCollection;
    060    import org.jfree.chart.entity.XYItemEntity;
    061    import org.jfree.chart.labels.XYToolTipGenerator;
    062    import org.jfree.chart.plot.CrosshairState;
    063    import org.jfree.chart.plot.PlotOrientation;
    064    import org.jfree.chart.plot.PlotRenderingInfo;
    065    import org.jfree.chart.plot.XYPlot;
    066    import org.jfree.data.Range;
    067    import org.jfree.data.general.DatasetUtilities;
    068    import org.jfree.data.xy.IntervalXYDataset;
    069    import org.jfree.data.xy.TableXYDataset;
    070    import org.jfree.data.xy.XYDataset;
    071    import org.jfree.ui.RectangleEdge;
    072
    073    /**
    074     * A bar renderer that displays the series items stacked.
    075     * The dataset used together with this renderer must be a
    076     * {@link org.jfree.data.xy.IntervalXYDataset} and a
    077     * {@link org.jfree.data.xy.TableXYDataset}. For example, the
    078     * dataset class {@link org.jfree.data.xy.CategoryTableXYDataset}
    079     * implements both interfaces.
    080     *
    081     * @author andreas.schroeder
    082     */
    083    public class StackedXYBarRenderer extends XYBarRenderer
    084                                      implements Serializable {
    085
    086        /** For serialization. */
    087        private static final long serialVersionUID = -7049101055533436444L;
    088
    089        /**
    090         * Creates a new renderer.
    091         */
    092        public StackedXYBarRenderer() {
    093            super();
    094        }
    095
    096        /**
    097         * Creates a new renderer.
    098         *
    099         * @param margin  the percentual amount of the bars that are cut away.
    100         */
    101        public StackedXYBarRenderer(double margin) {
    102            super(margin);
    103        }
    104
    105        /**
    106         * Initialises the renderer and returns a state object that should be
    107         * passed to all subsequent calls to the drawItem() method. Here there is
    108         * nothing to do.
    109         *
    110         * @param g2  the graphics device.
    111         * @param dataArea  the area inside the axes.
    112         * @param plot  the plot.
    113         * @param data  the data.
    114         * @param info  an optional info collection object to return data back to
    115         *              the caller.
    116         *
    117         * @return A state object.
    118         */
    119        public XYItemRendererState initialise(Graphics2D g2,
    120                                              Rectangle2D dataArea,
    121                                              XYPlot plot,
    122                                              XYDataset data,
    123                                              PlotRenderingInfo info) {
    124            return new XYBarRendererState(info);
    125        }
    126
    127        /**
    128         * Returns the range of values the renderer requires to display all the
    129         * items from the specified dataset.
    130         *
    131         * @param dataset  the dataset (<code>null</code> permitted).
    132         *
    133         * @return The range (<code>null</code> if the dataset is <code>null</code>
    134         *         or empty).
    135         */
    136        public Range findRangeBounds(XYDataset dataset) {
    137            if (dataset != null) {
    138                return DatasetUtilities.findStackedRangeBounds(
    139                    (TableXYDataset) dataset
    140                );
    141            }
    142            else {
    143                return null;
    144            }
    145        }
    146
    147        /**
    148         * Draws the visual representation of a single data item.
    149         *
    150         * @param g2  the graphics device.
    151         * @param state  the renderer state.
    152         * @param dataArea  the area within which the plot is being drawn.
    153         * @param info  collects information about the drawing.
    154         * @param plot  the plot (can be used to obtain standard color information
    155         *              etc).
    156         * @param domainAxis  the domain axis.
    157         * @param rangeAxis  the range axis.
    158         * @param dataset  the dataset.
    159         * @param series  the series index (zero-based).
    160         * @param item  the item index (zero-based).
    161         * @param crosshairState  crosshair information for the plot
    162         *                        (<code>null</code> permitted).
    163         * @param pass  the pass index.
    164         */
    165        public void drawItem(Graphics2D g2,
    166                             XYItemRendererState state,
    167                             Rectangle2D dataArea,
    168                             PlotRenderingInfo info,
    169                             XYPlot plot,
    170                             ValueAxis domainAxis,
    171                             ValueAxis rangeAxis,
    172                             XYDataset dataset,
    173                             int series,
    174                             int item,
    175                             CrosshairState crosshairState,
    176                             int pass) {
    177            if (!(dataset instanceof IntervalXYDataset
    178                    && dataset instanceof TableXYDataset)) {
    179                String message = "dataset (type " + dataset.getClass().getName()
    180                    + ") has wrong type:";
    181                boolean and = false;
    182                if (!IntervalXYDataset.class.isAssignableFrom(dataset.getClass())) {
    183                    message += " it is no IntervalXYDataset";
    184                    and = true;
    185                }
    186                if (!TableXYDataset.class.isAssignableFrom(dataset.getClass())) {
    187                    if (and) {
    188                        message += " and";
    189                    }
    190                    message += " it is no TableXYDataset";
    191                }
    192
    193                throw new IllegalArgumentException(message);
    194            }
    195
    196            IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
    197            double value = intervalDataset.getYValue(series, item);
    198            if (Double.isNaN(value)) {
    199                return;
    200            }
    201
    202            double positiveBase = 0.0;
    203            double negativeBase = 0.0;
    204
    205            for (int i = 0; i < series; i++) {
    206                double v = dataset.getYValue(i, item);
    207                if (!Double.isNaN(v)) {
    208                    if (v > 0) {
    209                        positiveBase = positiveBase + v;
    210                    }
    211                    else {
    212                        negativeBase = negativeBase + v;
    213                    }
    214                }
    215            }
    216
    217            double translatedBase;
    218            double translatedValue;
    219            RectangleEdge edgeR = plot.getRangeAxisEdge();
    220            if (value > 0.0) {
    221                translatedBase = rangeAxis.valueToJava2D(
    222                    positiveBase, dataArea, edgeR
    223                );
    224                translatedValue = rangeAxis.valueToJava2D(
    225                    positiveBase + value, dataArea, edgeR
    226                );
    227            }
    228            else {
    229                translatedBase = rangeAxis.valueToJava2D(
    230                    negativeBase, dataArea, edgeR
    231                );
    232                translatedValue = rangeAxis.valueToJava2D(
    233                    negativeBase + value, dataArea, edgeR
    234                );
    235            }
    236
    237            RectangleEdge edgeD = plot.getDomainAxisEdge();
    238            double startX = intervalDataset.getStartXValue(series, item);
    239            if (Double.isNaN(startX)) {
    240                return;
    241            }
    242            double translatedStartX = domainAxis.valueToJava2D(
    243                startX, dataArea, edgeD
    244            );
    245
    246            double endX = intervalDataset.getEndXValue(series, item);
    247            if (Double.isNaN(endX)) {
    248                return;
    249            }
    250            double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD);
    251
    252            double translatedWidth = Math.max(
    253                1, Math.abs(translatedEndX - translatedStartX)
    254            );
    255            double translatedHeight = Math.abs(translatedValue - translatedBase);
    256            if (getMargin() > 0.0) {
    257                double cut = translatedWidth * getMargin();
    258                translatedWidth = translatedWidth - cut;
    259                translatedStartX = translatedStartX + cut / 2;
    260            }
    261
    262            Rectangle2D bar = null;
    263            PlotOrientation orientation = plot.getOrientation();
    264            if (orientation == PlotOrientation.HORIZONTAL) {
    265                bar = new Rectangle2D.Double(
    266                    Math.min(translatedBase, translatedValue),
    267                    translatedEndX,
    268                    translatedHeight,
    269                    translatedWidth
    270                );
    271            }
    272            else if (orientation == PlotOrientation.VERTICAL) {
    273                bar = new Rectangle2D.Double(
    274                    translatedStartX,
    275                    Math.min(translatedBase, translatedValue),
    276                    translatedWidth,
    277                    translatedHeight
    278                );
    279            }
    280
    281            g2.setPaint(getItemPaint(series, item));
    282            g2.fill(bar);
    283            if (isDrawBarOutline()
    284                    && Math.abs(translatedEndX - translatedStartX) > 3) {
    285                g2.setStroke(getItemStroke(series, item));
    286                g2.setPaint(getItemOutlinePaint(series, item));
    287                g2.draw(bar);
    288            }
    289
    290            // add an entity for the item...
    291            if (info != null) {
    292                EntityCollection entities = info.getOwner().getEntityCollection();
    293                if (entities != null) {
    294                    String tip = null;
    295                    XYToolTipGenerator generator
    296                        = getToolTipGenerator(series, item);
    297                    if (generator != null) {
    298                        tip = generator.generateToolTip(dataset, series, item);
    299                    }
    300                    String url = null;
    301                    if (getURLGenerator() != null) {
    302                        url = getURLGenerator().generateURL(dataset, series, item);
    303                    }
    304                    XYItemEntity entity = new XYItemEntity(
    305                        bar, dataset, series, item, tip, url
    306                    );
    307                    entities.add(entity);
    308                }
    309            }
    310        }
    311
    312    }