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