    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     * LayeredBarRenderer.java
    029     * -----------------------
    030     * (C) Copyright 2003-2005, by Arnaud Lelievre and Contributors.
    031     *
    032     * Original Author:  Arnaud Lelievre (for Garden);
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * Changes
    036     * -------
    037     * 28-Aug-2003 : Version 1 (AL);
    038     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    039     * 07-Oct-2003 : Added renderer state (DG);
    040     * 21-Oct-2003 : Bar width moved to renderer state (DG);
    041     * 05-Nov-2004 : Modified drawItem() signature (DG);
    042     * 20-Apr-2005 : Renamed CategoryLabelGenerator
    043     *               --> CategoryItemLabelGenerator (DG);
    044     * 17-Nov-2005 : Added support for gradient paint (DG);
    045     *
    046     */
    047
    048    package org.jfree.chart.renderer.category;
    049
    050    import java.awt.GradientPaint;
    051    import java.awt.Graphics2D;
    052    import java.awt.Paint;
    053    import java.awt.Stroke;
    054    import java.awt.geom.Rectangle2D;
    055    import java.io.Serializable;
    056
    057    import org.jfree.chart.axis.CategoryAxis;
    058    import org.jfree.chart.axis.ValueAxis;
    059    import org.jfree.chart.entity.CategoryItemEntity;
    060    import org.jfree.chart.entity.EntityCollection;
    061    import org.jfree.chart.labels.CategoryItemLabelGenerator;
    062    import org.jfree.chart.labels.CategoryToolTipGenerator;
    063    import org.jfree.chart.plot.CategoryPlot;
    064    import org.jfree.chart.plot.PlotOrientation;
    065    import org.jfree.data.category.CategoryDataset;
    066    import org.jfree.ui.GradientPaintTransformer;
    067    import org.jfree.ui.RectangleEdge;
    068    import org.jfree.util.ObjectList;
    069
    070    /**
    071     * A {@link CategoryItemRenderer} that represents data using bars which are
    072     * superimposed.
    073     *
    074     * @author Arnaud Lelievre
    075     */
    076    public class LayeredBarRenderer extends BarRenderer
    077                                    implements Serializable {
    078
    079        /** For serialization. */
    080        private static final long serialVersionUID = -8716572894780469487L;
    081
    082        /** A list of the width of each series bar. */
    083        protected ObjectList seriesBarWidthList;
    084
    085        /**
    086         * Default constructor.
    087         */
    088        public LayeredBarRenderer() {
    089            super();
    090            this.seriesBarWidthList = new ObjectList();
    091        }
    092
    093        /**
    094         * Returns the bar width for a series, or <code>Double.NaN</code> if no
    095         * width has been set.
    096         *
    097         * @param series  the series index (zero based).
    098         *
    099         * @return The width for the series (1.0=100%, it is the maximum).
    100         */
    101        public double getSeriesBarWidth(int series) {
    102            double result = Double.NaN;
    103            Number n = (Number) this.seriesBarWidthList.get(series);
    104            if (n != null) {
    105                result = n.doubleValue();
    106            }
    107            return result;
    108        }
    109
    110        /**
    111         * Sets the width of the bars of a series.
    112         *
    113         * @param series  the series index (zero based).
    114         * @param width  the width of the series bar in percentage (1.0=100%, it is
    115         *               the maximum).
    116         */
    117        public void setSeriesBarWidth(int series, double width) {
    118            this.seriesBarWidthList.set(series, new Double(width));
    119        }
    120
    121        /**
    122         * Calculates the bar width and stores it in the renderer state.
    123         *
    124         * @param plot  the plot.
    125         * @param dataArea  the data area.
    126         * @param rendererIndex  the renderer index.
    127         * @param state  the renderer state.
    128         */
    129        protected void calculateBarWidth(CategoryPlot plot,
    130                                         Rectangle2D dataArea,
    131                                         int rendererIndex,
    132                                         CategoryItemRendererState state) {
    133
    134            // calculate the bar width
    135            CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
    136            CategoryDataset dataset = plot.getDataset(rendererIndex);
    137            if (dataset != null) {
    138                int columns = dataset.getColumnCount();
    139                int rows = dataset.getRowCount();
    140                double space = 0.0;
    141                PlotOrientation orientation = plot.getOrientation();
    142                if (orientation == PlotOrientation.HORIZONTAL) {
    143                    space = dataArea.getHeight();
    144                }
    145                else if (orientation == PlotOrientation.VERTICAL) {
    146                    space = dataArea.getWidth();
    147                }
    148                double categoryMargin = 0.0;
    149                if (columns > 1) {
    150                    categoryMargin = domainAxis.getCategoryMargin();
    151                }
    152                double used = space * (1 - domainAxis.getLowerMargin()
    153                    - domainAxis.getUpperMargin() - categoryMargin);
    154                if ((rows * columns) > 0) {
    155                    state.setBarWidth(used / (dataset.getColumnCount()));
    156                }
    157                else {
    158                    state.setBarWidth(used);
    159                }
    160            }
    161        }
    162
    163        /**
    164         * Draws the bar for one item in the dataset.
    165         *
    166         * @param g2  the graphics device.
    167         * @param state  the renderer state.
    168         * @param dataArea  the plot area.
    169         * @param plot  the plot.
    170         * @param domainAxis  the domain (category) axis.
    171         * @param rangeAxis  the range (value) axis.
    172         * @param data  the data.
    173         * @param row  the row index (zero-based).
    174         * @param column  the column index (zero-based).
    175         * @param pass  the pass index.
    176         */
    177        public void drawItem(Graphics2D g2,
    178                             CategoryItemRendererState state,
    179                             Rectangle2D dataArea,
    180                             CategoryPlot plot,
    181                             CategoryAxis domainAxis,
    182                             ValueAxis rangeAxis,
    183                             CategoryDataset data,
    184                             int row,
    185                             int column,
    186                             int pass) {
    187
    188            PlotOrientation orientation = plot.getOrientation();
    189            if (orientation == PlotOrientation.HORIZONTAL) {
    190                drawHorizontalItem(g2, state, dataArea, plot, domainAxis, rangeAxis,
    191                    data, row, column);
    192            }
    193            else if (orientation == PlotOrientation.VERTICAL) {
    194                drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis,
    195                    data, row, column);
    196            }
    197
    198        }
    199
    200        /**
    201         * Draws the bar for a single (series, category) data item.
    202         *
    203         * @param g2  the graphics device.
    204         * @param state  the renderer state.
    205         * @param dataArea  the data area.
    206         * @param plot  the plot.
    207         * @param domainAxis  the domain axis.
    208         * @param rangeAxis  the range axis.
    209         * @param data  the data.
    210         * @param row  the row index (zero-based).
    211         * @param column  the column index (zero-based).
    212         */
    213        protected void drawHorizontalItem(Graphics2D g2,
    214                                          CategoryItemRendererState state,
    215                                          Rectangle2D dataArea,
    216                                          CategoryPlot plot,
    217                                          CategoryAxis domainAxis,
    218                                          ValueAxis rangeAxis,
    219                                          CategoryDataset data,
    220                                          int row,
    221                                          int column) {
    222
    223            // nothing is drawn for null values...
    224            Number dataValue = data.getValue(row, column);
    225            if (dataValue == null) {
    226                return;
    227            }
    228
    229            // X
    230            double value = dataValue.doubleValue();
    231            double base = 0.0;
    232            double lclip = getLowerClip();
    233            double uclip = getUpperClip();
    234            if (uclip <= 0.0) {  // cases 1, 2, 3 and 4
    235                if (value >= uclip) {
    236                    return; // bar is not visible
    237                }
    238                base = uclip;
    239                if (value <= lclip) {
    240                    value = lclip;
    241                }
    242            }
    243            else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
    244                if (value >= uclip) {
    245                    value = uclip;
    246                }
    247                else {
    248                    if (value <= lclip) {
    249                        value = lclip;
    250                    }
    251                }
    252            }
    253            else { // cases 9, 10, 11 and 12
    254                if (value <= lclip) {
    255                    return; // bar is not visible
    256                }
    257                base = lclip;
    258                if (value >= uclip) {
    259                    value = uclip;
    260                }
    261            }
    262
    263            RectangleEdge edge = plot.getRangeAxisEdge();
    264            double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
    265            double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
    266            double rectX = Math.min(transX1, transX2);
    267            double rectWidth = Math.abs(transX2 - transX1);
    268
    269            // Y
    270            double rectY = domainAxis.getCategoryMiddle(column, getColumnCount(),
    271                    dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0;
    272
    273            int seriesCount = getRowCount();
    274
    275            // draw the bar...
    276            double shift = 0.0;
    277            double rectHeight = 0.0;
    278            double widthFactor = 1.0;
    279            double seriesBarWidth = getSeriesBarWidth(row);
    280            if (!Double.isNaN(seriesBarWidth)) {
    281                widthFactor = seriesBarWidth;
    282            }
    283            rectHeight = widthFactor * state.getBarWidth();
    284            rectY = rectY + (1 - widthFactor) * state.getBarWidth() / 2.0;
    285            if (seriesCount > 1) {
    286                shift = rectHeight * 0.20 / (seriesCount - 1);
    287            }
    288
    289            Rectangle2D bar = new Rectangle2D.Double(rectX,
    290                    (rectY + ((seriesCount - 1 - row) * shift)), rectWidth,
    291                    (rectHeight - (seriesCount - 1 - row) * shift * 2));
    292
    293            Paint itemPaint = getItemPaint(row, column);
    294            GradientPaintTransformer t = getGradientPaintTransformer();
    295            if (t != null && itemPaint instanceof GradientPaint) {
    296                itemPaint = t.transform((GradientPaint) itemPaint, bar);
    297            }
    298            g2.setPaint(itemPaint);
    299            g2.fill(bar);
    300
    301            // draw the outline...
    302            if (isDrawBarOutline()
    303                    && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
    304                Stroke stroke = getItemOutlineStroke(row, column);
    305                Paint paint = getItemOutlinePaint(row, column);
    306                if (stroke != null && paint != null) {
    307                    g2.setStroke(stroke);
    308                    g2.setPaint(paint);
    309                    g2.draw(bar);
    310                }
    311            }
    312
    313            CategoryItemLabelGenerator generator
    314                = getItemLabelGenerator(row, column);
    315            if (generator != null && isItemLabelVisible(row, column)) {
    316                drawItemLabel(g2, data, row, column, plot, generator, bar,
    317                        (transX1 > transX2));
    318            }
    319
    320            // collect entity and tool tip information...
    321            if (state.getInfo() != null) {
    322                EntityCollection entities = state.getEntityCollection();
    323                if (entities != null) {
    324                    String tip = null;
    325                    CategoryToolTipGenerator tipster
    326                        = getToolTipGenerator(row, column);
    327                    if (tipster != null) {
    328                        tip = tipster.generateToolTip(data, row, column);
    329                    }
    330                    String url = null;
    331                    if (getItemURLGenerator(row, column) != null) {
    332                        url = getItemURLGenerator(row, column).generateURL(data,
    333                                row, column);
    334                    }
    335                    CategoryItemEntity entity = new CategoryItemEntity(bar, tip,
    336                            url, data, row, data.getColumnKey(column), column);
    337                    entities.add(entity);
    338                }
    339            }
    340        }
    341
    342        /**
    343         * Draws the bar for a single (series, category) data item.
    344         *
    345         * @param g2  the graphics device.
    346         * @param state  the renderer state.
    347         * @param dataArea  the data area.
    348         * @param plot  the plot.
    349         * @param domainAxis  the domain axis.
    350         * @param rangeAxis  the range axis.
    351         * @param data  the data.
    352         * @param row  the row index (zero-based).
    353         * @param column  the column index (zero-based).
    354         */
    355        protected void drawVerticalItem(Graphics2D g2,
    356                                        CategoryItemRendererState state,
    357                                        Rectangle2D dataArea,
    358                                        CategoryPlot plot,
    359                                        CategoryAxis domainAxis,
    360                                        ValueAxis rangeAxis,
    361                                        CategoryDataset data,
    362                                        int row,
    363                                        int column) {
    364
    365            // nothing is drawn for null values...
    366            Number dataValue = data.getValue(row, column);
    367            if (dataValue == null) {
    368                return;
    369            }
    370
    371            // BAR X
    372            double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(),
    373                    dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0;
    374
    375            int seriesCount = getRowCount();
    376
    377            // BAR Y
    378            double value = dataValue.doubleValue();
    379            double base = 0.0;
    380            double lclip = getLowerClip();
    381            double uclip = getUpperClip();
    382
    383            if (uclip <= 0.0) {  // cases 1, 2, 3 and 4
    384                if (value >= uclip) {
    385                    return; // bar is not visible
    386                }
    387                base = uclip;
    388                if (value <= lclip) {
    389                    value = lclip;
    390                }
    391            }
    392            else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
    393                if (value >= uclip) {
    394                    value = uclip;
    395                }
    396                else {
    397                    if (value <= lclip) {
    398                        value = lclip;
    399                    }
    400                }
    401            }
    402            else { // cases 9, 10, 11 and 12
    403                if (value <= lclip) {
    404                    return; // bar is not visible
    405                }
    406                base = getLowerClip();
    407                if (value >= uclip) {
    408                   value = uclip;
    409                }
    410            }
    411
    412            RectangleEdge edge = plot.getRangeAxisEdge();
    413            double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge);
    414            double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge);
    415            double rectY = Math.min(transY2, transY1);
    416
    417            double rectWidth = state.getBarWidth();
    418            double rectHeight = Math.abs(transY2 - transY1);
    419
    420            // draw the bar...
    421            double shift = 0.0;
    422            rectWidth = 0.0;
    423            double widthFactor = 1.0;
    424            double seriesBarWidth = getSeriesBarWidth(row);
    425            if (!Double.isNaN(seriesBarWidth)) {
    426                widthFactor = seriesBarWidth;
    427            }
    428            rectWidth = widthFactor * state.getBarWidth();
    429            rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0;
    430            if (seriesCount > 1) {
    431                // needs to be improved !!!
    432                shift = rectWidth * 0.20 / (seriesCount - 1);
    433            }
    434
    435            Rectangle2D bar = new Rectangle2D.Double(
    436                (rectX + ((seriesCount - 1 - row) * shift)), rectY,
    437                (rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight);
    438            Paint itemPaint = getItemPaint(row, column);
    439            GradientPaintTransformer t = getGradientPaintTransformer();
    440            if (t != null && itemPaint instanceof GradientPaint) {
    441                itemPaint = t.transform((GradientPaint) itemPaint, bar);
    442            }
    443            g2.setPaint(itemPaint);
    444            g2.fill(bar);
    445
    446            // draw the outline...
    447            if (isDrawBarOutline()
    448                    && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
    449                Stroke stroke = getItemOutlineStroke(row, column);
    450                Paint paint = getItemOutlinePaint(row, column);
    451                if (stroke != null && paint != null) {
    452                    g2.setStroke(stroke);
    453                    g2.setPaint(paint);
    454                    g2.draw(bar);
    455                }
    456            }
    457
    458            // draw the item labels if there are any...
    459            double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
    460            double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
    461
    462            CategoryItemLabelGenerator generator
    463                = getItemLabelGenerator(row, column);
    464            if (generator != null && isItemLabelVisible(row, column)) {
    465                drawItemLabel(g2, data, row, column, plot, generator, bar,
    466                        (transX1 > transX2));
    467            }
    468
    469            // collect entity and tool tip information...
    470            if (state.getInfo() != null) {
    471                EntityCollection entities = state.getEntityCollection();
    472                if (entities != null) {
    473                    String tip = null;
    474                    CategoryToolTipGenerator tipster
    475                        = getToolTipGenerator(row, column);
    476                    if (tipster != null) {
    477                        tip = tipster.generateToolTip(data, row, column);
    478                    }
    479                    String url = null;
    480                    if (getItemURLGenerator(row, column) != null) {
    481                        url = getItemURLGenerator(row, column).generateURL(
    482                            data, row, column);
    483                    }
    484                    CategoryItemEntity entity = new CategoryItemEntity(bar, tip,
    485                            url, data, row, data.getColumnKey(column), column);
    486                    entities.add(entity);
    487                }
    488            }
    489        }
    490
    491    }