    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     * YIntervalRenderer.java
    029     * ----------------------
    030     * (C) Copyright 2002-2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes
    036     * -------
    037     * 05-Nov-2002 : Version 1 (DG);
    038     * 25-Mar-2003 : Implemented Serializable (DG);
    039     * 01-May-2003 : Modified drawItem() method signature (DG);
    040     * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
    041     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    042     * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
    043     * 27-Sep-2004 : Access double values from dataset (DG);
    044     * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
    045     * 11-Apr-2008 : New override for findRangeBounds() (DG);
    046     * 26-May-2008 : Added item label support (DG);
    047     *
    048     */
    049
    050    package org.jfree.chart.renderer.xy;
    051
    052    import java.awt.Font;
    053    import java.awt.Graphics2D;
    054    import java.awt.Paint;
    055    import java.awt.Shape;
    056    import java.awt.Stroke;
    057    import java.awt.geom.Line2D;
    058    import java.awt.geom.Point2D;
    059    import java.awt.geom.Rectangle2D;
    060    import java.io.Serializable;
    061
    062    import org.jfree.chart.axis.ValueAxis;
    063    import org.jfree.chart.entity.EntityCollection;
    064    import org.jfree.chart.entity.XYItemEntity;
    065    import org.jfree.chart.event.RendererChangeEvent;
    066    import org.jfree.chart.labels.ItemLabelPosition;
    067    import org.jfree.chart.labels.XYItemLabelGenerator;
    068    import org.jfree.chart.labels.XYToolTipGenerator;
    069    import org.jfree.chart.plot.CrosshairState;
    070    import org.jfree.chart.plot.PlotOrientation;
    071    import org.jfree.chart.plot.PlotRenderingInfo;
    072    import org.jfree.chart.plot.XYPlot;
    073    import org.jfree.data.Range;
    074    import org.jfree.data.general.DatasetUtilities;
    075    import org.jfree.data.xy.IntervalXYDataset;
    076    import org.jfree.data.xy.XYDataset;
    077    import org.jfree.text.TextUtilities;
    078    import org.jfree.ui.RectangleEdge;
    079    import org.jfree.util.ObjectUtilities;
    080    import org.jfree.util.PublicCloneable;
    081    import org.jfree.util.ShapeUtilities;
    082
    083    /**
    084     * A renderer that draws a line connecting the start and end Y values for an
    085     * {@link XYPlot}.
    086     */
    087    public class YIntervalRenderer extends AbstractXYItemRenderer
    088            implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
    089
    090            /** For serialization. */
    091        private static final long serialVersionUID = -2951586537224143260L;
    092
    093        /**
    094         * An additional item label generator.  If this is non-null, the item
    095         * label generated will be displayed near the lower y-value at the
    096         * position given by getNegativeItemLabelPosition().
    097         *
    098         * @since 1.0.10
    099         */
    100        private XYItemLabelGenerator additionalItemLabelGenerator;
    101
    102        /**
    103         * The default constructor.
    104         */
    105        public YIntervalRenderer() {
    106            super();
    107            this.additionalItemLabelGenerator = null;
    108        }
    109
    110        /**
    111         * Returns the generator for the item labels that appear near the lower
    112         * y-value.
    113         *
    114         * @return The generator (possibly <code>null</code>).
    115         *
    116         * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator)
    117         *
    118         * @since 1.0.10
    119         */
    120        public XYItemLabelGenerator getAdditionalItemLabelGenerator() {
    121            return this.additionalItemLabelGenerator;
    122        }
    123
    124        /**
    125         * Sets the generator for the item labels that appear near the lower
    126         * y-value and sends a {@link RendererChangeEvent} to all registered
    127         * listeners.  If this is set to <code>null</code>, no item labels will be
    128         * drawn.
    129         *
    130         * @param generator  the generator (<code>null</code> permitted).
    131         *
    132         * @see #getAdditionalItemLabelGenerator()
    133         *
    134         * @since 1.0.10
    135         */
    136        public void setAdditionalItemLabelGenerator(
    137                    XYItemLabelGenerator generator) {
    138            this.additionalItemLabelGenerator = generator;
    139            fireChangeEvent();
    140        }
    141
    142        /**
    143         * Returns the range of values the renderer requires to display all the
    144         * items from the specified dataset.
    145         *
    146         * @param dataset  the dataset (<code>null</code> permitted).
    147         *
    148         * @return The range (<code>null</code> if the dataset is <code>null</code>
    149         *         or empty).
    150         */
    151        public Range findRangeBounds(XYDataset dataset) {
    152            if (dataset != null) {
    153                return DatasetUtilities.findRangeBounds(dataset, true);
    154            }
    155            else {
    156                return null;
    157            }
    158        }
    159
    160        /**
    161         * Draws the visual representation of a single data item.
    162         *
    163         * @param g2  the graphics device.
    164         * @param state  the renderer state.
    165         * @param dataArea  the area within which the plot is being drawn.
    166         * @param info  collects information about the drawing.
    167         * @param plot  the plot (can be used to obtain standard color
    168         *              information etc).
    169         * @param domainAxis  the domain axis.
    170         * @param rangeAxis  the range axis.
    171         * @param dataset  the dataset.
    172         * @param series  the series index (zero-based).
    173         * @param item  the item index (zero-based).
    174         * @param crosshairState  crosshair information for the plot
    175         *                        (<code>null</code> permitted).
    176         * @param pass  the pass index (ignored here).
    177         */
    178        public void drawItem(Graphics2D g2,
    179                             XYItemRendererState state,
    180                             Rectangle2D dataArea,
    181                             PlotRenderingInfo info,
    182                             XYPlot plot,
    183                             ValueAxis domainAxis,
    184                             ValueAxis rangeAxis,
    185                             XYDataset dataset,
    186                             int series,
    187                             int item,
    188                             CrosshairState crosshairState,
    189                             int pass) {
    190
    191            // setup for collecting optional entity info...
    192            Shape entityArea = null;
    193            EntityCollection entities = null;
    194            if (info != null) {
    195                entities = info.getOwner().getEntityCollection();
    196            }
    197
    198            IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
    199
    200            double x = intervalDataset.getXValue(series, item);
    201            double yLow   = intervalDataset.getStartYValue(series, item);
    202            double yHigh  = intervalDataset.getEndYValue(series, item);
    203
    204            RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    205            RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
    206
    207            double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
    208            double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation);
    209            double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation);
    210
    211            Paint p = getItemPaint(series, item);
    212            Stroke s = getItemStroke(series, item);
    213
    214            Line2D line = null;
    215            Shape shape = getItemShape(series, item);
    216            Shape top = null;
    217            Shape bottom = null;
    218            PlotOrientation orientation = plot.getOrientation();
    219            if (orientation == PlotOrientation.HORIZONTAL) {
    220                line = new Line2D.Double(yyLow, xx, yyHigh, xx);
    221                top = ShapeUtilities.createTranslatedShape(shape, yyHigh, xx);
    222                bottom = ShapeUtilities.createTranslatedShape(shape, yyLow, xx);
    223            }
    224            else if (orientation == PlotOrientation.VERTICAL) {
    225                line = new Line2D.Double(xx, yyLow, xx, yyHigh);
    226                top = ShapeUtilities.createTranslatedShape(shape, xx, yyHigh);
    227                bottom = ShapeUtilities.createTranslatedShape(shape, xx, yyLow);
    228            }
    229            g2.setPaint(p);
    230            g2.setStroke(s);
    231            g2.draw(line);
    232
    233            g2.fill(top);
    234            g2.fill(bottom);
    235
    236            // for item labels, we have a special case because there is the
    237            // possibility to draw (a) the regular item label near to just the
    238            // upper y-value, or (b) the regular item label near the upper y-value
    239            // PLUS an additional item label near the lower y-value.
    240            if (isItemLabelVisible(series, item)) {
    241                drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh,
    242                        false);
    243                drawAdditionalItemLabel(g2, orientation, dataset, series, item,
    244                            xx, yyLow);
    245            }
    246
    247            // add an entity for the item...
    248            if (entities != null) {
    249                if (entityArea == null) {
    250                    entityArea = line.getBounds();
    251                }
    252                String tip = null;
    253                XYToolTipGenerator generator = getToolTipGenerator(series, item);
    254                if (generator != null) {
    255                    tip = generator.generateToolTip(dataset, series, item);
    256                }
    257                String url = null;
    258                if (getURLGenerator() != null) {
    259                    url = getURLGenerator().generateURL(dataset, series, item);
    260                }
    261                XYItemEntity entity = new XYItemEntity(entityArea, dataset, series,
    262                        item, tip, url);
    263                entities.add(entity);
    264            }
    265
    266        }
    267
    268        /**
    269         * Draws an item label.
    270         *
    271         * @param g2  the graphics device.
    272         * @param orientation  the orientation.
    273         * @param dataset  the dataset.
    274         * @param series  the series index (zero-based).
    275         * @param item  the item index (zero-based).
    276         * @param x  the x coordinate (in Java2D space).
    277         * @param y  the y coordinate (in Java2D space).
    278         * @param negative  indicates a negative value (which affects the item
    279         *                  label position).
    280         */
    281        private void drawAdditionalItemLabel(Graphics2D g2,
    282                    PlotOrientation orientation, XYDataset dataset, int series,
    283                    int item, double x, double y) {
    284
    285            if (this.additionalItemLabelGenerator == null) {
    286                    return;
    287            }
    288
    289            Font labelFont = getItemLabelFont(series, item);
    290            Paint paint = getItemLabelPaint(series, item);
    291            g2.setFont(labelFont);
    292            g2.setPaint(paint);
    293            String label = this.additionalItemLabelGenerator.generateLabel(dataset,
    294                            series, item);
    295
    296            ItemLabelPosition position = getNegativeItemLabelPosition(series, item);
    297            Point2D anchorPoint = calculateLabelAnchorPoint(
    298                    position.getItemLabelAnchor(), x, y, orientation);
    299            TextUtilities.drawRotatedString(label, g2,
    300                    (float) anchorPoint.getX(), (float) anchorPoint.getY(),
    301                    position.getTextAnchor(), position.getAngle(),
    302                    position.getRotationAnchor());
    303        }
    304
    305        /**
    306         * Tests this renderer for equality with an arbitrary object.
    307         *
    308         * @param obj  the object (<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 YIntervalRenderer)) {
    317                    return false;
    318            }
    319            YIntervalRenderer that = (YIntervalRenderer) obj;
    320            if (!ObjectUtilities.equal(this.additionalItemLabelGenerator,
    321                            that.additionalItemLabelGenerator)) {
    322                    return false;
    323            }
    324            return super.equals(obj);
    325        }
    326
    327        /**
    328         * Returns a clone of the renderer.
    329         *
    330         * @return A clone.
    331         *
    332         * @throws CloneNotSupportedException  if the renderer cannot be cloned.
    333         */
    334        public Object clone() throws CloneNotSupportedException {
    335            return super.clone();
    336        }
    337
    338    }