001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2014, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * ----------------------
028 * YIntervalRenderer.java
029 * ----------------------
030 * (C) Copyright 2002-2014, 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 * 27-Mar-2009 : Updated findRangeBounds() (DG);
048 *
049 */
050
051package org.jfree.chart.renderer.xy;
052
053import java.awt.Font;
054import java.awt.Graphics2D;
055import java.awt.Paint;
056import java.awt.Shape;
057import java.awt.Stroke;
058import java.awt.geom.Line2D;
059import java.awt.geom.Point2D;
060import java.awt.geom.Rectangle2D;
061import java.io.Serializable;
062
063import org.jfree.chart.axis.ValueAxis;
064import org.jfree.chart.entity.EntityCollection;
065import org.jfree.chart.event.RendererChangeEvent;
066import org.jfree.chart.labels.ItemLabelPosition;
067import org.jfree.chart.labels.XYItemLabelGenerator;
068import org.jfree.chart.plot.CrosshairState;
069import org.jfree.chart.plot.PlotOrientation;
070import org.jfree.chart.plot.PlotRenderingInfo;
071import org.jfree.chart.plot.XYPlot;
072import org.jfree.data.Range;
073import org.jfree.data.xy.IntervalXYDataset;
074import org.jfree.data.xy.XYDataset;
075import org.jfree.text.TextUtilities;
076import org.jfree.ui.RectangleEdge;
077import org.jfree.util.ObjectUtilities;
078import org.jfree.util.PublicCloneable;
079import org.jfree.util.ShapeUtilities;
080
081/**
082 * A renderer that draws a line connecting the start and end Y values for an
083 * {@link XYPlot}.  The example shown here is generated by the
084 * <code>YIntervalRendererDemo1.java</code> program included in the JFreeChart
085 * demo collection:
086 * <br><br>
087 * <img src="../../../../../images/YIntervalRendererSample.png"
088 * alt="YIntervalRendererSample.png">
089 */
090public class YIntervalRenderer extends AbstractXYItemRenderer
091        implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
092
093    /** For serialization. */
094    private static final long serialVersionUID = -2951586537224143260L;
095
096    /**
097     * An additional item label generator.  If this is non-null, the item
098     * label generated will be displayed near the lower y-value at the
099     * position given by getNegativeItemLabelPosition().
100     *
101     * @since 1.0.10
102     */
103    private XYItemLabelGenerator additionalItemLabelGenerator;
104
105    /**
106     * The default constructor.
107     */
108    public YIntervalRenderer() {
109        super();
110        this.additionalItemLabelGenerator = null;
111    }
112
113    /**
114     * Returns the generator for the item labels that appear near the lower
115     * y-value.
116     *
117     * @return The generator (possibly <code>null</code>).
118     *
119     * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator)
120     *
121     * @since 1.0.10
122     */
123    public XYItemLabelGenerator getAdditionalItemLabelGenerator() {
124        return this.additionalItemLabelGenerator;
125    }
126
127    /**
128     * Sets the generator for the item labels that appear near the lower
129     * y-value and sends a {@link RendererChangeEvent} to all registered
130     * listeners.  If this is set to <code>null</code>, no item labels will be
131     * drawn.
132     *
133     * @param generator  the generator (<code>null</code> permitted).
134     *
135     * @see #getAdditionalItemLabelGenerator()
136     *
137     * @since 1.0.10
138     */
139    public void setAdditionalItemLabelGenerator(
140            XYItemLabelGenerator generator) {
141        this.additionalItemLabelGenerator = generator;
142        fireChangeEvent();
143    }
144
145    /**
146     * Returns the range of values the renderer requires to display all the
147     * items from the specified dataset.
148     *
149     * @param dataset  the dataset (<code>null</code> permitted).
150     *
151     * @return The range (<code>null</code> if the dataset is <code>null</code>
152     *         or empty).
153     */
154    @Override
155    public Range findRangeBounds(XYDataset dataset) {
156        return findRangeBounds(dataset, true);
157    }
158
159    /**
160     * Draws the visual representation of a single data item.
161     *
162     * @param g2  the graphics device.
163     * @param state  the renderer state.
164     * @param dataArea  the area within which the plot is being drawn.
165     * @param info  collects information about the drawing.
166     * @param plot  the plot (can be used to obtain standard color
167     *              information etc).
168     * @param domainAxis  the domain axis.
169     * @param rangeAxis  the range axis.
170     * @param dataset  the dataset.
171     * @param series  the series index (zero-based).
172     * @param item  the item index (zero-based).
173     * @param crosshairState  crosshair information for the plot
174     *                        (<code>null</code> permitted).
175     * @param pass  the pass index (ignored here).
176     */
177    @Override
178    public void drawItem(Graphics2D g2, XYItemRendererState state,
179            Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
180            ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset,
181            int series, int item, CrosshairState crosshairState, int pass) {
182
183        // setup for collecting optional entity info...
184        EntityCollection entities = null;
185        if (info != null) {
186            entities = info.getOwner().getEntityCollection();
187        }
188
189        IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
190
191        double x = intervalDataset.getXValue(series, item);
192        double yLow   = intervalDataset.getStartYValue(series, item);
193        double yHigh  = intervalDataset.getEndYValue(series, item);
194
195        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
196        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
197
198        double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
199        double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation);
200        double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation);
201
202        Paint p = getItemPaint(series, item);
203        Stroke s = getItemStroke(series, item);
204
205        Line2D line = null;
206        Shape shape = getItemShape(series, item);
207        Shape top = null;
208        Shape bottom = null;
209        PlotOrientation orientation = plot.getOrientation();
210        if (orientation == PlotOrientation.HORIZONTAL) {
211            line = new Line2D.Double(yyLow, xx, yyHigh, xx);
212            top = ShapeUtilities.createTranslatedShape(shape, yyHigh, xx);
213            bottom = ShapeUtilities.createTranslatedShape(shape, yyLow, xx);
214        }
215        else if (orientation == PlotOrientation.VERTICAL) {
216            line = new Line2D.Double(xx, yyLow, xx, yyHigh);
217            top = ShapeUtilities.createTranslatedShape(shape, xx, yyHigh);
218            bottom = ShapeUtilities.createTranslatedShape(shape, xx, yyLow);
219        } else {
220            throw new IllegalStateException();
221        }
222        g2.setPaint(p);
223        g2.setStroke(s);
224        g2.draw(line);
225
226        g2.fill(top);
227        g2.fill(bottom);
228
229        // for item labels, we have a special case because there is the
230        // possibility to draw (a) the regular item label near to just the
231        // upper y-value, or (b) the regular item label near the upper y-value
232        // PLUS an additional item label near the lower y-value.
233        if (isItemLabelVisible(series, item)) {
234            drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh,
235                    false);
236            drawAdditionalItemLabel(g2, orientation, dataset, series, item,
237                    xx, yyLow);
238        }
239
240        // add an entity for the item...
241        if (entities != null) {
242            addEntity(entities, line.getBounds(), dataset, series, item, 0.0,
243                    0.0);
244        }
245
246    }
247
248    /**
249     * Draws an item label.
250     *
251     * @param g2  the graphics device.
252     * @param orientation  the orientation.
253     * @param dataset  the dataset.
254     * @param series  the series index (zero-based).
255     * @param item  the item index (zero-based).
256     * @param x  the x coordinate (in Java2D space).
257     * @param y  the y coordinate (in Java2D space).
258     */
259    private void drawAdditionalItemLabel(Graphics2D g2,
260            PlotOrientation orientation, XYDataset dataset, int series,
261            int item, double x, double y) {
262
263        if (this.additionalItemLabelGenerator == null) {
264            return;
265        }
266
267        Font labelFont = getItemLabelFont(series, item);
268        Paint paint = getItemLabelPaint(series, item);
269        g2.setFont(labelFont);
270        g2.setPaint(paint);
271        String label = this.additionalItemLabelGenerator.generateLabel(dataset,
272                series, item);
273
274        ItemLabelPosition position = getNegativeItemLabelPosition(series, item);
275        Point2D anchorPoint = calculateLabelAnchorPoint(
276                position.getItemLabelAnchor(), x, y, orientation);
277        TextUtilities.drawRotatedString(label, g2,
278                (float) anchorPoint.getX(), (float) anchorPoint.getY(),
279                position.getTextAnchor(), position.getAngle(),
280                position.getRotationAnchor());
281    }
282
283    /**
284     * Tests this renderer for equality with an arbitrary object.
285     *
286     * @param obj  the object (<code>null</code> permitted).
287     *
288     * @return A boolean.
289     */
290    @Override
291    public boolean equals(Object obj) {
292        if (obj == this) {
293            return true;
294        }
295        if (!(obj instanceof YIntervalRenderer)) {
296            return false;
297        }
298        YIntervalRenderer that = (YIntervalRenderer) obj;
299        if (!ObjectUtilities.equal(this.additionalItemLabelGenerator,
300                that.additionalItemLabelGenerator)) {
301            return false;
302        }
303        return super.equals(obj);
304    }
305
306    /**
307     * Returns a clone of the renderer.
308     *
309     * @return A clone.
310     *
311     * @throws CloneNotSupportedException  if the renderer cannot be cloned.
312     */
313    @Override
314    public Object clone() throws CloneNotSupportedException {
315        return super.clone();
316    }
317
318}