0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ------------------
0028: * XYBarRenderer.java
0029: * ------------------
0030: * (C) Copyright 2001-2007, by Object Refinery Limited.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Richard Atkinson;
0034: * Christian W. Zuckschwerdt;
0035: * Bill Kelemen;
0036: *
0037: * $Id: XYBarRenderer.java,v 1.14.2.17 2007/06/15 12:43:24 mungady Exp $
0038: *
0039: * Changes
0040: * -------
0041: * 13-Dec-2001 : Version 1, makes VerticalXYBarPlot class redundant (DG);
0042: * 23-Jan-2002 : Added DrawInfo parameter to drawItem() method (DG);
0043: * 09-Apr-2002 : Removed the translated zero from the drawItem method. Override
0044: * the initialise() method to calculate it (DG);
0045: * 24-May-2002 : Incorporated tooltips into chart entities (DG);
0046: * 25-Jun-2002 : Removed redundant import (DG);
0047: * 05-Aug-2002 : Small modification to drawItem method to support URLs for HTML
0048: * image maps (RA);
0049: * 25-Mar-2003 : Implemented Serializable (DG);
0050: * 01-May-2003 : Modified drawItem() method signature (DG);
0051: * 30-Jul-2003 : Modified entity constructor (CZ);
0052: * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
0053: * 24-Aug-2003 : Added null checks in drawItem (BK);
0054: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
0055: * 07-Oct-2003 : Added renderer state (DG);
0056: * 05-Dec-2003 : Changed call to obtain outline paint (DG);
0057: * 10-Feb-2004 : Added state class, updated drawItem() method to make
0058: * cut-and-paste overriding easier, and replaced property change
0059: * with RendererChangeEvent (DG);
0060: * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
0061: * 26-Apr-2004 : Added gradient paint transformer (DG);
0062: * 19-May-2004 : Fixed bug (879709) with bar zero value for secondary axis (DG);
0063: * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
0064: * getYValue() (DG);
0065: * 01-Sep-2004 : Added a flag to control whether or not the bar outlines are
0066: * drawn (DG);
0067: * 03-Sep-2004 : Added option to use y-interval from dataset to determine the
0068: * length of the bars (DG);
0069: * 08-Sep-2004 : Added equals() method and updated clone() method (DG);
0070: * 26-Jan-2005 : Added override for getLegendItem() method (DG);
0071: * 20-Apr-2005 : Use generators for label tooltips and URLs (DG);
0072: * 19-May-2005 : Added minimal item label implementation - needs improving (DG);
0073: * 14-Oct-2005 : Fixed rendering problem with inverted axes (DG);
0074: * ------------- JFREECHART 1.0.x ---------------------------------------------
0075: * 21-Jun-2006 : Improved item label handling - see bug 1501768 (DG);
0076: * 24-Aug-2006 : Added crosshair support (DG);
0077: * 13-Dec-2006 : Updated getLegendItems() to return gradient paint
0078: * transformer (DG);
0079: * 02-Feb-2007 : Changed setUseYInterval() to only notify when the flag
0080: * changes (DG);
0081: * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
0082: * 09-Feb-2007 : Updated getLegendItem() to observe drawBarOutline flag (DG);
0083: * 05-Mar-2007 : Applied patch 1671126 by Sergei Ivanov, to fix rendering with
0084: * LogarithmicAxis (DG);
0085: * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
0086: * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
0087: * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
0088: * 15-Jun-2007 : Changed default for drawBarOutline to false (DG);
0089: *
0090: */
0091:
0092: package org.jfree.chart.renderer.xy;
0093:
0094: import java.awt.Font;
0095: import java.awt.GradientPaint;
0096: import java.awt.Graphics2D;
0097: import java.awt.Paint;
0098: import java.awt.Shape;
0099: import java.awt.Stroke;
0100: import java.awt.geom.Point2D;
0101: import java.awt.geom.Rectangle2D;
0102: import java.io.IOException;
0103: import java.io.ObjectInputStream;
0104: import java.io.ObjectOutputStream;
0105: import java.io.Serializable;
0106:
0107: import org.jfree.chart.LegendItem;
0108: import org.jfree.chart.axis.ValueAxis;
0109: import org.jfree.chart.entity.EntityCollection;
0110: import org.jfree.chart.entity.XYItemEntity;
0111: import org.jfree.chart.event.RendererChangeEvent;
0112: import org.jfree.chart.labels.ItemLabelAnchor;
0113: import org.jfree.chart.labels.ItemLabelPosition;
0114: import org.jfree.chart.labels.XYItemLabelGenerator;
0115: import org.jfree.chart.labels.XYSeriesLabelGenerator;
0116: import org.jfree.chart.labels.XYToolTipGenerator;
0117: import org.jfree.chart.plot.CrosshairState;
0118: import org.jfree.chart.plot.PlotOrientation;
0119: import org.jfree.chart.plot.PlotRenderingInfo;
0120: import org.jfree.chart.plot.XYPlot;
0121: import org.jfree.data.Range;
0122: import org.jfree.data.general.DatasetUtilities;
0123: import org.jfree.data.xy.IntervalXYDataset;
0124: import org.jfree.data.xy.XYDataset;
0125: import org.jfree.io.SerialUtilities;
0126: import org.jfree.text.TextUtilities;
0127: import org.jfree.ui.GradientPaintTransformer;
0128: import org.jfree.ui.RectangleEdge;
0129: import org.jfree.ui.StandardGradientPaintTransformer;
0130: import org.jfree.util.ObjectUtilities;
0131: import org.jfree.util.PublicCloneable;
0132: import org.jfree.util.ShapeUtilities;
0133:
0134: /**
0135: * A renderer that draws bars on an {@link XYPlot} (requires an
0136: * {@link IntervalXYDataset}).
0137: */
0138: public class XYBarRenderer extends AbstractXYItemRenderer implements
0139: XYItemRenderer, Cloneable, PublicCloneable, Serializable {
0140:
0141: /** For serialization. */
0142: private static final long serialVersionUID = 770559577251370036L;
0143:
0144: /**
0145: * The state class used by this renderer.
0146: */
0147: protected class XYBarRendererState extends XYItemRendererState {
0148:
0149: /** Base for bars against the range axis, in Java 2D space. */
0150: private double g2Base;
0151:
0152: /**
0153: * Creates a new state object.
0154: *
0155: * @param info the plot rendering info.
0156: */
0157: public XYBarRendererState(PlotRenderingInfo info) {
0158: super (info);
0159: }
0160:
0161: /**
0162: * Returns the base (range) value in Java 2D space.
0163: *
0164: * @return The base value.
0165: */
0166: public double getG2Base() {
0167: return this .g2Base;
0168: }
0169:
0170: /**
0171: * Sets the range axis base in Java2D space.
0172: *
0173: * @param value the value.
0174: */
0175: public void setG2Base(double value) {
0176: this .g2Base = value;
0177: }
0178: }
0179:
0180: /** The default base value for the bars. */
0181: private double base;
0182:
0183: /**
0184: * A flag that controls whether the bars use the y-interval supplied by the
0185: * dataset.
0186: */
0187: private boolean useYInterval;
0188:
0189: /** Percentage margin (to reduce the width of bars). */
0190: private double margin;
0191:
0192: /** A flag that controls whether or not bar outlines are drawn. */
0193: private boolean drawBarOutline;
0194:
0195: /**
0196: * An optional class used to transform gradient paint objects to fit each
0197: * bar.
0198: */
0199: private GradientPaintTransformer gradientPaintTransformer;
0200:
0201: /**
0202: * The shape used to represent a bar in each legend item (this should never
0203: * be <code>null</code>).
0204: */
0205: private transient Shape legendBar;
0206:
0207: /**
0208: * The fallback position if a positive item label doesn't fit inside the
0209: * bar.
0210: */
0211: private ItemLabelPosition positiveItemLabelPositionFallback;
0212:
0213: /**
0214: * The fallback position if a negative item label doesn't fit inside the
0215: * bar.
0216: */
0217: private ItemLabelPosition negativeItemLabelPositionFallback;
0218:
0219: /**
0220: * The default constructor.
0221: */
0222: public XYBarRenderer() {
0223: this (0.0);
0224: }
0225:
0226: /**
0227: * Constructs a new renderer.
0228: *
0229: * @param margin the percentage amount to trim from the width of each bar.
0230: */
0231: public XYBarRenderer(double margin) {
0232: super ();
0233: this .margin = margin;
0234: this .base = 0.0;
0235: this .useYInterval = false;
0236: this .gradientPaintTransformer = new StandardGradientPaintTransformer();
0237: this .drawBarOutline = false;
0238: this .legendBar = new Rectangle2D.Double(-3.0, -5.0, 6.0, 10.0);
0239: }
0240:
0241: /**
0242: * Returns the base value for the bars.
0243: *
0244: * @return The base value for the bars.
0245: *
0246: * @see #setBase(double)
0247: */
0248: public double getBase() {
0249: return this .base;
0250: }
0251:
0252: /**
0253: * Sets the base value for the bars and sends a {@link RendererChangeEvent}
0254: * to all registered listeners. The base value is not used if the dataset's
0255: * y-interval is being used to determine the bar length.
0256: *
0257: * @param base the new base value.
0258: *
0259: * @see #getBase()
0260: * @see #getUseYInterval()
0261: */
0262: public void setBase(double base) {
0263: this .base = base;
0264: notifyListeners(new RendererChangeEvent(this ));
0265: }
0266:
0267: /**
0268: * Returns a flag that determines whether the y-interval from the dataset is
0269: * used to calculate the length of each bar.
0270: *
0271: * @return A boolean.
0272: *
0273: * @see #setUseYInterval(boolean)
0274: */
0275: public boolean getUseYInterval() {
0276: return this .useYInterval;
0277: }
0278:
0279: /**
0280: * Sets the flag that determines whether the y-interval from the dataset is
0281: * used to calculate the length of each bar, and sends a
0282: * {@link RendererChangeEvent} to all registered listeners.
0283: *
0284: * @param use the flag.
0285: *
0286: * @see #getUseYInterval()
0287: */
0288: public void setUseYInterval(boolean use) {
0289: if (this .useYInterval != use) {
0290: this .useYInterval = use;
0291: notifyListeners(new RendererChangeEvent(this ));
0292: }
0293: }
0294:
0295: /**
0296: * Returns the margin which is a percentage amount by which the bars are
0297: * trimmed.
0298: *
0299: * @return The margin.
0300: *
0301: * @see #setMargin(double)
0302: */
0303: public double getMargin() {
0304: return this .margin;
0305: }
0306:
0307: /**
0308: * Sets the percentage amount by which the bars are trimmed and sends a
0309: * {@link RendererChangeEvent} to all registered listeners.
0310: *
0311: * @param margin the new margin.
0312: *
0313: * @see #getMargin()
0314: */
0315: public void setMargin(double margin) {
0316: this .margin = margin;
0317: notifyListeners(new RendererChangeEvent(this ));
0318: }
0319:
0320: /**
0321: * Returns a flag that controls whether or not bar outlines are drawn.
0322: *
0323: * @return A boolean.
0324: *
0325: * @see #setDrawBarOutline(boolean)
0326: */
0327: public boolean isDrawBarOutline() {
0328: return this .drawBarOutline;
0329: }
0330:
0331: /**
0332: * Sets the flag that controls whether or not bar outlines are drawn and
0333: * sends a {@link RendererChangeEvent} to all registered listeners.
0334: *
0335: * @param draw the flag.
0336: *
0337: * @see #isDrawBarOutline()
0338: */
0339: public void setDrawBarOutline(boolean draw) {
0340: this .drawBarOutline = draw;
0341: notifyListeners(new RendererChangeEvent(this ));
0342: }
0343:
0344: /**
0345: * Returns the gradient paint transformer (an object used to transform
0346: * gradient paint objects to fit each bar).
0347: *
0348: * @return A transformer (<code>null</code> possible).
0349: *
0350: * @see #setGradientPaintTransformer(GradientPaintTransformer)
0351: */
0352: public GradientPaintTransformer getGradientPaintTransformer() {
0353: return this .gradientPaintTransformer;
0354: }
0355:
0356: /**
0357: * Sets the gradient paint transformer and sends a
0358: * {@link RendererChangeEvent} to all registered listeners.
0359: *
0360: * @param transformer the transformer (<code>null</code> permitted).
0361: *
0362: * @see #getGradientPaintTransformer()
0363: */
0364: public void setGradientPaintTransformer(
0365: GradientPaintTransformer transformer) {
0366: this .gradientPaintTransformer = transformer;
0367: notifyListeners(new RendererChangeEvent(this ));
0368: }
0369:
0370: /**
0371: * Returns the shape used to represent bars in each legend item.
0372: *
0373: * @return The shape used to represent bars in each legend item (never
0374: * <code>null</code>).
0375: *
0376: * @see #setLegendBar(Shape)
0377: */
0378: public Shape getLegendBar() {
0379: return this .legendBar;
0380: }
0381:
0382: /**
0383: * Sets the shape used to represent bars in each legend item and sends a
0384: * {@link RendererChangeEvent} to all registered listeners.
0385: *
0386: * @param bar the bar shape (<code>null</code> not permitted).
0387: *
0388: * @see #getLegendBar()
0389: */
0390: public void setLegendBar(Shape bar) {
0391: if (bar == null) {
0392: throw new IllegalArgumentException("Null 'bar' argument.");
0393: }
0394: this .legendBar = bar;
0395: notifyListeners(new RendererChangeEvent(this ));
0396: }
0397:
0398: /**
0399: * Returns the fallback position for positive item labels that don't fit
0400: * within a bar.
0401: *
0402: * @return The fallback position (<code>null</code> possible).
0403: *
0404: * @see #setPositiveItemLabelPositionFallback(ItemLabelPosition)
0405: * @since 1.0.2
0406: */
0407: public ItemLabelPosition getPositiveItemLabelPositionFallback() {
0408: return this .positiveItemLabelPositionFallback;
0409: }
0410:
0411: /**
0412: * Sets the fallback position for positive item labels that don't fit
0413: * within a bar, and sends a {@link RendererChangeEvent} to all registered
0414: * listeners.
0415: *
0416: * @param position the position (<code>null</code> permitted).
0417: *
0418: * @see #getPositiveItemLabelPositionFallback()
0419: * @since 1.0.2
0420: */
0421: public void setPositiveItemLabelPositionFallback(
0422: ItemLabelPosition position) {
0423: this .positiveItemLabelPositionFallback = position;
0424: notifyListeners(new RendererChangeEvent(this ));
0425: }
0426:
0427: /**
0428: * Returns the fallback position for negative item labels that don't fit
0429: * within a bar.
0430: *
0431: * @return The fallback position (<code>null</code> possible).
0432: *
0433: * @see #setNegativeItemLabelPositionFallback(ItemLabelPosition)
0434: * @since 1.0.2
0435: */
0436: public ItemLabelPosition getNegativeItemLabelPositionFallback() {
0437: return this .negativeItemLabelPositionFallback;
0438: }
0439:
0440: /**
0441: * Sets the fallback position for negative item labels that don't fit
0442: * within a bar, and sends a {@link RendererChangeEvent} to all registered
0443: * listeners.
0444: *
0445: * @param position the position (<code>null</code> permitted).
0446: *
0447: * @see #getNegativeItemLabelPositionFallback()
0448: * @since 1.0.2
0449: */
0450: public void setNegativeItemLabelPositionFallback(
0451: ItemLabelPosition position) {
0452: this .negativeItemLabelPositionFallback = position;
0453: notifyListeners(new RendererChangeEvent(this ));
0454: }
0455:
0456: /**
0457: * Initialises the renderer and returns a state object that should be
0458: * passed to all subsequent calls to the drawItem() method. Here we
0459: * calculate the Java2D y-coordinate for zero, since all the bars have
0460: * their bases fixed at zero.
0461: *
0462: * @param g2 the graphics device.
0463: * @param dataArea the area inside the axes.
0464: * @param plot the plot.
0465: * @param dataset the data.
0466: * @param info an optional info collection object to return data back to
0467: * the caller.
0468: *
0469: * @return A state object.
0470: */
0471: public XYItemRendererState initialise(Graphics2D g2,
0472: Rectangle2D dataArea, XYPlot plot, XYDataset dataset,
0473: PlotRenderingInfo info) {
0474:
0475: XYBarRendererState state = new XYBarRendererState(info);
0476: ValueAxis rangeAxis = plot.getRangeAxisForDataset(plot
0477: .indexOf(dataset));
0478: state.setG2Base(rangeAxis.valueToJava2D(this .base, dataArea,
0479: plot.getRangeAxisEdge()));
0480: return state;
0481:
0482: }
0483:
0484: /**
0485: * Returns a default legend item for the specified series. Subclasses
0486: * should override this method to generate customised items.
0487: *
0488: * @param datasetIndex the dataset index (zero-based).
0489: * @param series the series index (zero-based).
0490: *
0491: * @return A legend item for the series.
0492: */
0493: public LegendItem getLegendItem(int datasetIndex, int series) {
0494: LegendItem result = null;
0495: XYPlot xyplot = getPlot();
0496: if (xyplot != null) {
0497: XYDataset dataset = xyplot.getDataset(datasetIndex);
0498: if (dataset != null) {
0499: XYSeriesLabelGenerator lg = getLegendItemLabelGenerator();
0500: String label = lg.generateLabel(dataset, series);
0501: String description = label;
0502: String toolTipText = null;
0503: if (getLegendItemToolTipGenerator() != null) {
0504: toolTipText = getLegendItemToolTipGenerator()
0505: .generateLabel(dataset, series);
0506: }
0507: String urlText = null;
0508: if (getLegendItemURLGenerator() != null) {
0509: urlText = getLegendItemURLGenerator()
0510: .generateLabel(dataset, series);
0511: }
0512: Shape shape = this .legendBar;
0513: Paint paint = lookupSeriesPaint(series);
0514: Paint outlinePaint = lookupSeriesOutlinePaint(series);
0515: Stroke outlineStroke = lookupSeriesOutlineStroke(series);
0516: if (this .drawBarOutline) {
0517: result = new LegendItem(label, description,
0518: toolTipText, urlText, shape, paint,
0519: outlineStroke, outlinePaint);
0520: } else {
0521: result = new LegendItem(label, description,
0522: toolTipText, urlText, shape, paint);
0523: }
0524: result.setDataset(dataset);
0525: result.setDatasetIndex(datasetIndex);
0526: result.setSeriesKey(dataset.getSeriesKey(series));
0527: result.setSeriesIndex(series);
0528: if (getGradientPaintTransformer() != null) {
0529: result
0530: .setFillPaintTransformer(getGradientPaintTransformer());
0531: }
0532: }
0533: }
0534: return result;
0535: }
0536:
0537: /**
0538: * Draws the visual representation of a single data item.
0539: *
0540: * @param g2 the graphics device.
0541: * @param state the renderer state.
0542: * @param dataArea the area within which the plot is being drawn.
0543: * @param info collects information about the drawing.
0544: * @param plot the plot (can be used to obtain standard color
0545: * information etc).
0546: * @param domainAxis the domain axis.
0547: * @param rangeAxis the range axis.
0548: * @param dataset the dataset.
0549: * @param series the series index (zero-based).
0550: * @param item the item index (zero-based).
0551: * @param crosshairState crosshair information for the plot
0552: * (<code>null</code> permitted).
0553: * @param pass the pass index.
0554: */
0555: public void drawItem(Graphics2D g2, XYItemRendererState state,
0556: Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
0557: ValueAxis domainAxis, ValueAxis rangeAxis,
0558: XYDataset dataset, int series, int item,
0559: CrosshairState crosshairState, int pass) {
0560:
0561: if (!getItemVisible(series, item)) {
0562: return;
0563: }
0564: IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
0565:
0566: double value0;
0567: double value1;
0568: if (this .useYInterval) {
0569: value0 = intervalDataset.getStartYValue(series, item);
0570: value1 = intervalDataset.getEndYValue(series, item);
0571: } else {
0572: value0 = this .base;
0573: value1 = intervalDataset.getYValue(series, item);
0574: }
0575: if (Double.isNaN(value0) || Double.isNaN(value1)) {
0576: return;
0577: }
0578: if (value0 <= value1) {
0579: if (!rangeAxis.getRange().intersects(value0, value1)) {
0580: return;
0581: }
0582: } else {
0583: if (!rangeAxis.getRange().intersects(value1, value0)) {
0584: return;
0585: }
0586: }
0587:
0588: double translatedValue0 = rangeAxis.valueToJava2D(value0,
0589: dataArea, plot.getRangeAxisEdge());
0590: double translatedValue1 = rangeAxis.valueToJava2D(value1,
0591: dataArea, plot.getRangeAxisEdge());
0592: double bottom = Math.min(translatedValue0, translatedValue1);
0593: double top = Math.max(translatedValue0, translatedValue1);
0594:
0595: double startX = intervalDataset.getStartXValue(series, item);
0596: if (Double.isNaN(startX)) {
0597: return;
0598: }
0599: double endX = intervalDataset.getEndXValue(series, item);
0600: if (Double.isNaN(endX)) {
0601: return;
0602: }
0603: if (startX <= endX) {
0604: if (!domainAxis.getRange().intersects(startX, endX)) {
0605: return;
0606: }
0607: } else {
0608: if (!domainAxis.getRange().intersects(endX, startX)) {
0609: return;
0610: }
0611: }
0612:
0613: RectangleEdge location = plot.getDomainAxisEdge();
0614: double translatedStartX = domainAxis.valueToJava2D(startX,
0615: dataArea, location);
0616: double translatedEndX = domainAxis.valueToJava2D(endX,
0617: dataArea, location);
0618:
0619: double translatedWidth = Math.max(1, Math.abs(translatedEndX
0620: - translatedStartX));
0621:
0622: if (getMargin() > 0.0) {
0623: double cut = translatedWidth * getMargin();
0624: translatedWidth = translatedWidth - cut;
0625: translatedStartX = translatedStartX + cut / 2;
0626: }
0627:
0628: Rectangle2D bar = null;
0629: PlotOrientation orientation = plot.getOrientation();
0630: if (orientation == PlotOrientation.HORIZONTAL) {
0631: // clip left and right bounds to data area
0632: bottom = Math.max(bottom, dataArea.getMinX());
0633: top = Math.min(top, dataArea.getMaxX());
0634: bar = new Rectangle2D.Double(bottom, Math.min(
0635: translatedStartX, translatedEndX), top - bottom,
0636: translatedWidth);
0637: } else if (orientation == PlotOrientation.VERTICAL) {
0638: // clip top and bottom bounds to data area
0639: bottom = Math.max(bottom, dataArea.getMinY());
0640: top = Math.min(top, dataArea.getMaxY());
0641: bar = new Rectangle2D.Double(Math.min(translatedStartX,
0642: translatedEndX), bottom, translatedWidth, top
0643: - bottom);
0644: }
0645:
0646: Paint itemPaint = getItemPaint(series, item);
0647: if (getGradientPaintTransformer() != null
0648: && itemPaint instanceof GradientPaint) {
0649: GradientPaint gp = (GradientPaint) itemPaint;
0650: itemPaint = getGradientPaintTransformer()
0651: .transform(gp, bar);
0652: }
0653: g2.setPaint(itemPaint);
0654: g2.fill(bar);
0655: if (isDrawBarOutline()
0656: && Math.abs(translatedEndX - translatedStartX) > 3) {
0657: Stroke stroke = getItemOutlineStroke(series, item);
0658: Paint paint = getItemOutlinePaint(series, item);
0659: if (stroke != null && paint != null) {
0660: g2.setStroke(stroke);
0661: g2.setPaint(paint);
0662: g2.draw(bar);
0663: }
0664: }
0665:
0666: if (isItemLabelVisible(series, item)) {
0667: XYItemLabelGenerator generator = getItemLabelGenerator(
0668: series, item);
0669: drawItemLabel(g2, dataset, series, item, plot, generator,
0670: bar, value1 < 0.0);
0671: }
0672:
0673: // update the crosshair point
0674: double x1 = (startX + endX) / 2.0;
0675: double y1 = dataset.getYValue(series, item);
0676: double transX1 = domainAxis.valueToJava2D(x1, dataArea,
0677: location);
0678: double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot
0679: .getRangeAxisEdge());
0680: int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
0681: int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
0682: updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
0683: rangeAxisIndex, transX1, transY1, plot.getOrientation());
0684:
0685: // add an entity for the item...
0686: if (info != null) {
0687: EntityCollection entities = info.getOwner()
0688: .getEntityCollection();
0689: if (entities != null) {
0690: String tip = null;
0691: XYToolTipGenerator generator = getToolTipGenerator(
0692: series, item);
0693: if (generator != null) {
0694: tip = generator.generateToolTip(dataset, series,
0695: item);
0696: }
0697: String url = null;
0698: if (getURLGenerator() != null) {
0699: url = getURLGenerator().generateURL(dataset,
0700: series, item);
0701: }
0702: XYItemEntity entity = new XYItemEntity(bar, dataset,
0703: series, item, tip, url);
0704: entities.add(entity);
0705: }
0706: }
0707:
0708: }
0709:
0710: /**
0711: * Draws an item label. This method is overridden so that the bar can be
0712: * used to calculate the label anchor point.
0713: *
0714: * @param g2 the graphics device.
0715: * @param dataset the dataset.
0716: * @param series the series index.
0717: * @param item the item index.
0718: * @param plot the plot.
0719: * @param generator the label generator.
0720: * @param bar the bar.
0721: * @param negative a flag indicating a negative value.
0722: */
0723: protected void drawItemLabel(Graphics2D g2, XYDataset dataset,
0724: int series, int item, XYPlot plot,
0725: XYItemLabelGenerator generator, Rectangle2D bar,
0726: boolean negative) {
0727:
0728: String label = generator.generateLabel(dataset, series, item);
0729: if (label == null) {
0730: return; // nothing to do
0731: }
0732:
0733: Font labelFont = getItemLabelFont(series, item);
0734: g2.setFont(labelFont);
0735: Paint paint = getItemLabelPaint(series, item);
0736: g2.setPaint(paint);
0737:
0738: // find out where to place the label...
0739: ItemLabelPosition position = null;
0740: if (!negative) {
0741: position = getPositiveItemLabelPosition(series, item);
0742: } else {
0743: position = getNegativeItemLabelPosition(series, item);
0744: }
0745:
0746: // work out the label anchor point...
0747: Point2D anchorPoint = calculateLabelAnchorPoint(position
0748: .getItemLabelAnchor(), bar, plot.getOrientation());
0749:
0750: if (isInternalAnchor(position.getItemLabelAnchor())) {
0751: Shape bounds = TextUtilities.calculateRotatedStringBounds(
0752: label, g2, (float) anchorPoint.getX(),
0753: (float) anchorPoint.getY(), position
0754: .getTextAnchor(), position.getAngle(),
0755: position.getRotationAnchor());
0756:
0757: if (bounds != null) {
0758: if (!bar.contains(bounds.getBounds2D())) {
0759: if (!negative) {
0760: position = getPositiveItemLabelPositionFallback();
0761: } else {
0762: position = getNegativeItemLabelPositionFallback();
0763: }
0764: if (position != null) {
0765: anchorPoint = calculateLabelAnchorPoint(
0766: position.getItemLabelAnchor(), bar,
0767: plot.getOrientation());
0768: }
0769: }
0770: }
0771:
0772: }
0773:
0774: if (position != null) {
0775: TextUtilities.drawRotatedString(label, g2,
0776: (float) anchorPoint.getX(), (float) anchorPoint
0777: .getY(), position.getTextAnchor(), position
0778: .getAngle(), position.getRotationAnchor());
0779: }
0780: }
0781:
0782: /**
0783: * Calculates the item label anchor point.
0784: *
0785: * @param anchor the anchor.
0786: * @param bar the bar.
0787: * @param orientation the plot orientation.
0788: *
0789: * @return The anchor point.
0790: */
0791: private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
0792: Rectangle2D bar, PlotOrientation orientation) {
0793:
0794: Point2D result = null;
0795: double offset = getItemLabelAnchorOffset();
0796: double x0 = bar.getX() - offset;
0797: double x1 = bar.getX();
0798: double x2 = bar.getX() + offset;
0799: double x3 = bar.getCenterX();
0800: double x4 = bar.getMaxX() - offset;
0801: double x5 = bar.getMaxX();
0802: double x6 = bar.getMaxX() + offset;
0803:
0804: double y0 = bar.getMaxY() + offset;
0805: double y1 = bar.getMaxY();
0806: double y2 = bar.getMaxY() - offset;
0807: double y3 = bar.getCenterY();
0808: double y4 = bar.getMinY() + offset;
0809: double y5 = bar.getMinY();
0810: double y6 = bar.getMinY() - offset;
0811:
0812: if (anchor == ItemLabelAnchor.CENTER) {
0813: result = new Point2D.Double(x3, y3);
0814: } else if (anchor == ItemLabelAnchor.INSIDE1) {
0815: result = new Point2D.Double(x4, y4);
0816: } else if (anchor == ItemLabelAnchor.INSIDE2) {
0817: result = new Point2D.Double(x4, y4);
0818: } else if (anchor == ItemLabelAnchor.INSIDE3) {
0819: result = new Point2D.Double(x4, y3);
0820: } else if (anchor == ItemLabelAnchor.INSIDE4) {
0821: result = new Point2D.Double(x4, y2);
0822: } else if (anchor == ItemLabelAnchor.INSIDE5) {
0823: result = new Point2D.Double(x4, y2);
0824: } else if (anchor == ItemLabelAnchor.INSIDE6) {
0825: result = new Point2D.Double(x3, y2);
0826: } else if (anchor == ItemLabelAnchor.INSIDE7) {
0827: result = new Point2D.Double(x2, y2);
0828: } else if (anchor == ItemLabelAnchor.INSIDE8) {
0829: result = new Point2D.Double(x2, y2);
0830: } else if (anchor == ItemLabelAnchor.INSIDE9) {
0831: result = new Point2D.Double(x2, y3);
0832: } else if (anchor == ItemLabelAnchor.INSIDE10) {
0833: result = new Point2D.Double(x2, y4);
0834: } else if (anchor == ItemLabelAnchor.INSIDE11) {
0835: result = new Point2D.Double(x2, y4);
0836: } else if (anchor == ItemLabelAnchor.INSIDE12) {
0837: result = new Point2D.Double(x3, y4);
0838: } else if (anchor == ItemLabelAnchor.OUTSIDE1) {
0839: result = new Point2D.Double(x5, y6);
0840: } else if (anchor == ItemLabelAnchor.OUTSIDE2) {
0841: result = new Point2D.Double(x6, y5);
0842: } else if (anchor == ItemLabelAnchor.OUTSIDE3) {
0843: result = new Point2D.Double(x6, y3);
0844: } else if (anchor == ItemLabelAnchor.OUTSIDE4) {
0845: result = new Point2D.Double(x6, y1);
0846: } else if (anchor == ItemLabelAnchor.OUTSIDE5) {
0847: result = new Point2D.Double(x5, y0);
0848: } else if (anchor == ItemLabelAnchor.OUTSIDE6) {
0849: result = new Point2D.Double(x3, y0);
0850: } else if (anchor == ItemLabelAnchor.OUTSIDE7) {
0851: result = new Point2D.Double(x1, y0);
0852: } else if (anchor == ItemLabelAnchor.OUTSIDE8) {
0853: result = new Point2D.Double(x0, y1);
0854: } else if (anchor == ItemLabelAnchor.OUTSIDE9) {
0855: result = new Point2D.Double(x0, y3);
0856: } else if (anchor == ItemLabelAnchor.OUTSIDE10) {
0857: result = new Point2D.Double(x0, y5);
0858: } else if (anchor == ItemLabelAnchor.OUTSIDE11) {
0859: result = new Point2D.Double(x1, y6);
0860: } else if (anchor == ItemLabelAnchor.OUTSIDE12) {
0861: result = new Point2D.Double(x3, y6);
0862: }
0863:
0864: return result;
0865:
0866: }
0867:
0868: /**
0869: * Returns <code>true</code> if the specified anchor point is inside a bar.
0870: *
0871: * @param anchor the anchor point.
0872: *
0873: * @return A boolean.
0874: */
0875: private boolean isInternalAnchor(ItemLabelAnchor anchor) {
0876: return anchor == ItemLabelAnchor.CENTER
0877: || anchor == ItemLabelAnchor.INSIDE1
0878: || anchor == ItemLabelAnchor.INSIDE2
0879: || anchor == ItemLabelAnchor.INSIDE3
0880: || anchor == ItemLabelAnchor.INSIDE4
0881: || anchor == ItemLabelAnchor.INSIDE5
0882: || anchor == ItemLabelAnchor.INSIDE6
0883: || anchor == ItemLabelAnchor.INSIDE7
0884: || anchor == ItemLabelAnchor.INSIDE8
0885: || anchor == ItemLabelAnchor.INSIDE9
0886: || anchor == ItemLabelAnchor.INSIDE10
0887: || anchor == ItemLabelAnchor.INSIDE11
0888: || anchor == ItemLabelAnchor.INSIDE12;
0889: }
0890:
0891: /**
0892: * Returns the lower and upper bounds (range) of the x-values in the
0893: * specified dataset. Since this renderer uses the x-interval in the
0894: * dataset, this is taken into account for the range.
0895: *
0896: * @param dataset the dataset (<code>null</code> permitted).
0897: *
0898: * @return The range (<code>null</code> if the dataset is
0899: * <code>null</code> or empty).
0900: */
0901: public Range findDomainBounds(XYDataset dataset) {
0902: if (dataset != null) {
0903: return DatasetUtilities.findDomainBounds(dataset, true);
0904: } else {
0905: return null;
0906: }
0907: }
0908:
0909: /**
0910: * Returns a clone of the renderer.
0911: *
0912: * @return A clone.
0913: *
0914: * @throws CloneNotSupportedException if the renderer cannot be cloned.
0915: */
0916: public Object clone() throws CloneNotSupportedException {
0917: XYBarRenderer result = (XYBarRenderer) super .clone();
0918: if (this .gradientPaintTransformer != null) {
0919: result.gradientPaintTransformer = (GradientPaintTransformer) ObjectUtilities
0920: .clone(this .gradientPaintTransformer);
0921: }
0922: result.legendBar = ShapeUtilities.clone(this .legendBar);
0923: return result;
0924: }
0925:
0926: /**
0927: * Tests this renderer for equality with an arbitrary object.
0928: *
0929: * @param obj the object to test against (<code>null</code> permitted).
0930: *
0931: * @return A boolean.
0932: */
0933: public boolean equals(Object obj) {
0934: if (obj == this ) {
0935: return true;
0936: }
0937: if (!(obj instanceof XYBarRenderer)) {
0938: return false;
0939: }
0940: if (!super .equals(obj)) {
0941: return false;
0942: }
0943: XYBarRenderer that = (XYBarRenderer) obj;
0944: if (this .base != that.base) {
0945: return false;
0946: }
0947: if (this .drawBarOutline != that.drawBarOutline) {
0948: return false;
0949: }
0950: if (this .margin != that.margin) {
0951: return false;
0952: }
0953: if (this .useYInterval != that.useYInterval) {
0954: return false;
0955: }
0956: if (!ObjectUtilities.equal(this .gradientPaintTransformer,
0957: that.gradientPaintTransformer)) {
0958: return false;
0959: }
0960: if (!ShapeUtilities.equal(this .legendBar, that.legendBar)) {
0961: return false;
0962: }
0963: if (!ObjectUtilities.equal(
0964: this .positiveItemLabelPositionFallback,
0965: that.positiveItemLabelPositionFallback)) {
0966: return false;
0967: }
0968: if (!ObjectUtilities.equal(
0969: this .negativeItemLabelPositionFallback,
0970: that.negativeItemLabelPositionFallback)) {
0971: return false;
0972: }
0973: return true;
0974: }
0975:
0976: /**
0977: * Provides serialization support.
0978: *
0979: * @param stream the input stream.
0980: *
0981: * @throws IOException if there is an I/O error.
0982: * @throws ClassNotFoundException if there is a classpath problem.
0983: */
0984: private void readObject(ObjectInputStream stream)
0985: throws IOException, ClassNotFoundException {
0986: stream.defaultReadObject();
0987: this .legendBar = SerialUtilities.readShape(stream);
0988: }
0989:
0990: /**
0991: * Provides serialization support.
0992: *
0993: * @param stream the output stream.
0994: *
0995: * @throws IOException if there is an I/O error.
0996: */
0997: private void writeObject(ObjectOutputStream stream)
0998: throws IOException {
0999: stream.defaultWriteObject();
1000: SerialUtilities.writeShape(this.legendBar, stream);
1001: }
1002:
1003: }
|