KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > category > BarRenderer


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ----------------
28  * BarRenderer.java
29  * ----------------
30  * (C) Copyright 2002-2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): Christian W. Zuckschwerdt;
34  *
35  * $Id: BarRenderer.java,v 1.13.2.10 2006/08/04 15:36:26 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 14-Mar-2002 : Version 1 (DG);
40  * 23-May-2002 : Added tooltip generator to renderer (DG);
41  * 29-May-2002 : Moved tooltip generator to abstract super-class (DG);
42  * 25-Jun-2002 : Changed constructor to protected and removed redundant
43  * code (DG);
44  * 26-Jun-2002 : Added axis to initialise method, and record upper and lower
45  * clip values (DG);
46  * 24-Sep-2002 : Added getLegendItem() method (DG);
47  * 09-Oct-2002 : Modified constructor to include URL generator (DG);
48  * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
49  * 10-Jan-2003 : Moved get/setItemMargin() method up from subclasses (DG);
50  * 17-Jan-2003 : Moved plot classes into a separate package (DG);
51  * 25-Mar-2003 : Implemented Serializable (DG);
52  * 01-May-2003 : Modified clipping to allow for dual axes and datasets (DG);
53  * 12-May-2003 : Merged horizontal and vertical bar renderers (DG);
54  * 12-Jun-2003 : Updates for item labels (DG);
55  * 30-Jul-2003 : Modified entity constructor (CZ);
56  * 02-Sep-2003 : Changed initialise method to fix bug 790407 (DG);
57  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
58  * 07-Oct-2003 : Added renderer state (DG);
59  * 27-Oct-2003 : Merged drawHorizontalItem() and drawVerticalItem()
60  * methods (DG);
61  * 28-Oct-2003 : Added support for gradient paint on bars (DG);
62  * 14-Nov-2003 : Added 'maxBarWidth' attribute (DG);
63  * 10-Feb-2004 : Small changes inside drawItem() method to ease cut-and-paste
64  * overriding (DG);
65  * 19-Mar-2004 : Fixed bug introduced with separation of tool tip and item
66  * label generators. Fixed equals() method (DG);
67  * 11-May-2004 : Fix for null pointer exception (bug id 951127) (DG);
68  * 05-Nov-2004 : Modified drawItem() signature (DG);
69  * 26-Jan-2005 : Provided override for getLegendItem() method (DG);
70  * 20-Apr-2005 : Generate legend labels, tooltips and URLs (DG);
71  * 18-May-2005 : Added configurable base value (DG);
72  * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
73  * 01-Dec-2005 : Update legend item to use/not use outline (DG);
74  * ------------: JFreeChart 1.0.0 ---------------------------------------------
75  * 06-Dec-2005 : Fixed bug 1374222 (JDK 1.4 specific code) (DG);
76  * 11-Jan-2006 : Fixed bug 1401856 (bad rendering for non-zero base) (DG);
77  * 04-Aug-2006 : Fixed bug 1467706 (missing item labels for zero value
78  * bars) (DG);
79  *
80  */

81
82 package org.jfree.chart.renderer.category;
83
84 import java.awt.BasicStroke JavaDoc;
85 import java.awt.Color JavaDoc;
86 import java.awt.Font JavaDoc;
87 import java.awt.GradientPaint JavaDoc;
88 import java.awt.Graphics2D JavaDoc;
89 import java.awt.Paint JavaDoc;
90 import java.awt.Shape JavaDoc;
91 import java.awt.Stroke JavaDoc;
92 import java.awt.geom.Line2D JavaDoc;
93 import java.awt.geom.Point2D JavaDoc;
94 import java.awt.geom.Rectangle2D JavaDoc;
95 import java.io.Serializable JavaDoc;
96
97 import org.jfree.chart.LegendItem;
98 import org.jfree.chart.axis.CategoryAxis;
99 import org.jfree.chart.axis.ValueAxis;
100 import org.jfree.chart.entity.EntityCollection;
101 import org.jfree.chart.event.RendererChangeEvent;
102 import org.jfree.chart.labels.CategoryItemLabelGenerator;
103 import org.jfree.chart.labels.ItemLabelAnchor;
104 import org.jfree.chart.labels.ItemLabelPosition;
105 import org.jfree.chart.plot.CategoryPlot;
106 import org.jfree.chart.plot.PlotOrientation;
107 import org.jfree.chart.plot.PlotRenderingInfo;
108 import org.jfree.data.Range;
109 import org.jfree.data.category.CategoryDataset;
110 import org.jfree.data.general.DatasetUtilities;
111 import org.jfree.text.TextUtilities;
112 import org.jfree.ui.GradientPaintTransformer;
113 import org.jfree.ui.RectangleEdge;
114 import org.jfree.ui.StandardGradientPaintTransformer;
115 import org.jfree.util.ObjectUtilities;
116 import org.jfree.util.PublicCloneable;
117
118 /**
119  * A {@link CategoryItemRenderer} that draws individual data items as bars.
120  */

121 public class BarRenderer extends AbstractCategoryItemRenderer
122                          implements Cloneable JavaDoc, PublicCloneable, Serializable JavaDoc {
123
124     /** For serialization. */
125     private static final long serialVersionUID = 6000649414965887481L;
126     
127     /** The default item margin percentage. */
128     public static final double DEFAULT_ITEM_MARGIN = 0.20;
129
130     /**
131      * Constant that controls the minimum width before a bar has an outline
132      * drawn.
133      */

134     public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0;
135
136     /** The margin between items (bars) within a category. */
137     private double itemMargin;
138
139     /** A flag that controls whether or not bar outlines are drawn. */
140     private boolean drawBarOutline;
141     
142     /** The maximum bar width as a percentage of the available space. */
143     private double maximumBarWidth;
144     
145     /** The minimum bar length (in Java2D units). */
146     private double minimumBarLength;
147     
148     /**
149      * An optional class used to transform gradient paint objects to fit each
150      * bar.
151      */

152     private GradientPaintTransformer gradientPaintTransformer;
153     
154     /**
155      * The fallback position if a positive item label doesn't fit inside the
156      * bar.
157      */

158     private ItemLabelPosition positiveItemLabelPositionFallback;
159     
160     /**
161      * The fallback position if a negative item label doesn't fit inside the
162      * bar.
163      */

164     private ItemLabelPosition negativeItemLabelPositionFallback;
165     
166     /** The upper clip (axis) value for the axis. */
167     private double upperClip;
168     // TODO: this needs to move into the renderer state
169

170     /** The lower clip (axis) value for the axis. */
171     private double lowerClip;
172     // TODO: this needs to move into the renderer state
173

174     /** The base value for the bars (defaults to 0.0). */
175     private double base;
176     
177     /**
178      * A flag that controls whether the base value is included in the range
179      * returned by the findRangeBounds() method.
180      */

181     private boolean includeBaseInRange;
182     
183     /**
184      * Creates a new bar renderer with default settings.
185      */

186     public BarRenderer() {
187         super();
188         this.base = 0.0;
189         this.includeBaseInRange = true;
190         this.itemMargin = DEFAULT_ITEM_MARGIN;
191         this.drawBarOutline = true;
192         this.maximumBarWidth = 1.0;
193             // 100 percent, so it will not apply unless changed
194
this.positiveItemLabelPositionFallback = null;
195         this.negativeItemLabelPositionFallback = null;
196         this.gradientPaintTransformer = new StandardGradientPaintTransformer();
197         this.minimumBarLength = 0.0;
198     }
199
200     /**
201      * Returns the base value for the bars.
202      *
203      * @return The base value for the bars.
204      */

205     public double getBase() {
206         return this.base;
207     }
208     
209     /**
210      * Sets the base value for the bars and sends a {@link RendererChangeEvent}
211      * to all registered listeners.
212      *
213      * @param base the new base value.
214      */

215     public void setBase(double base) {
216         this.base = base;
217         notifyListeners(new RendererChangeEvent(this));
218     }
219     
220     /**
221      * Returns the item margin as a percentage of the available space for all
222      * bars.
223      *
224      * @return The margin percentage (where 0.10 is ten percent).
225      */

226     public double getItemMargin() {
227         return this.itemMargin;
228     }
229
230     /**
231      * Sets the item margin and sends a {@link RendererChangeEvent} to all
232      * registered listeners. The value is expressed as a percentage of the
233      * available width for plotting all the bars, with the resulting amount to
234      * be distributed between all the bars evenly.
235      *
236      * @param percent the margin (where 0.10 is ten percent).
237      */

238     public void setItemMargin(double percent) {
239         this.itemMargin = percent;
240         notifyListeners(new RendererChangeEvent(this));
241     }
242
243     /**
244      * Returns a flag that controls whether or not bar outlines are drawn.
245      *
246      * @return A boolean.
247      */

248     public boolean isDrawBarOutline() {
249         return this.drawBarOutline;
250     }
251     
252     /**
253      * Sets the flag that controls whether or not bar outlines are drawn and
254      * sends a {@link RendererChangeEvent} to all registered listeners.
255      *
256      * @param draw the flag.
257      */

258     public void setDrawBarOutline(boolean draw) {
259         this.drawBarOutline = draw;
260         notifyListeners(new RendererChangeEvent(this));
261     }
262     
263     /**
264      * Returns the maximum bar width, as a percentage of the available drawing
265      * space.
266      *
267      * @return The maximum bar width.
268      */

269     public double getMaximumBarWidth() {
270         return this.maximumBarWidth;
271     }
272     
273     /**
274      * Sets the maximum bar width, which is specified as a percentage of the
275      * available space for all bars, and sends a {@link RendererChangeEvent} to
276      * all registered listeners.
277      *
278      * @param percent the percent (where 0.05 is five percent).
279      */

280     public void setMaximumBarWidth(double percent) {
281         this.maximumBarWidth = percent;
282         notifyListeners(new RendererChangeEvent(this));
283     }
284
285     /**
286      * Returns the minimum bar length (in Java2D units).
287      *
288      * @return The minimum bar length.
289      */

290     public double getMinimumBarLength() {
291         return this.minimumBarLength;
292     }
293     
294     /**
295      * Sets the minimum bar length and sends a {@link RendererChangeEvent} to
296      * all registered listeners. The minimum bar length is specified in Java2D
297      * units, and can be used to prevent bars that represent very small data
298      * values from disappearing when drawn on the screen.
299      *
300      * @param min the minimum bar length (in Java2D units).
301      */

302     public void setMinimumBarLength(double min) {
303         this.minimumBarLength = min;
304         notifyListeners(new RendererChangeEvent(this));
305     }
306     
307     /**
308      * Returns the gradient paint transformer (an object used to transform
309      * gradient paint objects to fit each bar.
310      *
311      * @return A transformer (<code>null</code> possible).
312      */

313     public GradientPaintTransformer getGradientPaintTransformer() {
314         return this.gradientPaintTransformer;
315     }
316     
317     /**
318      * Sets the gradient paint transformer and sends a
319      * {@link RendererChangeEvent} to all registered listeners.
320      *
321      * @param transformer the transformer (<code>null</code> permitted).
322      */

323     public void setGradientPaintTransformer(
324             GradientPaintTransformer transformer) {
325         this.gradientPaintTransformer = transformer;
326         notifyListeners(new RendererChangeEvent(this));
327     }
328     
329     /**
330      * Returns the fallback position for positive item labels that don't fit
331      * within a bar.
332      *
333      * @return The fallback position (<code>null</code> possible).
334      */

335     public ItemLabelPosition getPositiveItemLabelPositionFallback() {
336         return this.positiveItemLabelPositionFallback;
337     }
338     
339     /**
340      * Sets the fallback position for positive item labels that don't fit
341      * within a bar, and sends a {@link RendererChangeEvent} to all registered
342      * listeners.
343      *
344      * @param position the position (<code>null</code> permitted).
345      */

346     public void setPositiveItemLabelPositionFallback(
347             ItemLabelPosition position) {
348         this.positiveItemLabelPositionFallback = position;
349         notifyListeners(new RendererChangeEvent(this));
350     }
351     
352     /**
353      * Returns the fallback position for negative item labels that don't fit
354      * within a bar.
355      *
356      * @return The fallback position (<code>null</code> possible).
357      */

358     public ItemLabelPosition getNegativeItemLabelPositionFallback() {
359         return this.negativeItemLabelPositionFallback;
360     }
361     
362     /**
363      * Sets the fallback position for negative item labels that don't fit
364      * within a bar, and sends a {@link RendererChangeEvent} to all registered
365      * listeners.
366      *
367      * @param position the position (<code>null</code> permitted).
368      */

369     public void setNegativeItemLabelPositionFallback(
370             ItemLabelPosition position) {
371         this.negativeItemLabelPositionFallback = position;
372         notifyListeners(new RendererChangeEvent(this));
373     }
374     
375     /**
376      * Returns the flag that controls whether or not the base value for the
377      * bars is included in the range calculated by
378      * {@link #findRangeBounds(CategoryDataset)}.
379      *
380      * @return <code>true</code> if the base is included in the range, and
381      * <code>false</code> otherwise.
382      *
383      * @since 1.0.1
384      */

385     public boolean getIncludeBaseInRange() {
386         return this.includeBaseInRange;
387     }
388     
389     /**
390      * Sets the flag that controls whether or not the base value for the bars
391      * is included in the range calculated by
392      * {@link #findRangeBounds(CategoryDataset)}. If the flag is changed,
393      * a {@link RendererChangeEvent} is sent to all registered listeners.
394      *
395      * @param include the new value for the flag.
396      *
397      * @since 1.0.1
398      */

399     public void setIncludeBaseInRange(boolean include) {
400         if (this.includeBaseInRange != include) {
401             this.includeBaseInRange = include;
402             notifyListeners(new RendererChangeEvent(this));
403         }
404     }
405     
406     /**
407      * Returns the lower clip value. This value is recalculated in the
408      * initialise() method.
409      *
410      * @return The value.
411      */

412     public double getLowerClip() {
413         // TODO: this attribute should be transferred to the renderer state.
414
return this.lowerClip;
415     }
416
417     /**
418      * Returns the upper clip value. This value is recalculated in the
419      * initialise() method.
420      *
421      * @return The value.
422      */

423     public double getUpperClip() {
424         // TODO: this attribute should be transferred to the renderer state.
425
return this.upperClip;
426     }
427
428     /**
429      * Initialises the renderer and returns a state object that will be passed
430      * to subsequent calls to the drawItem method. This method gets called
431      * once at the start of the process of drawing a chart.
432      *
433      * @param g2 the graphics device.
434      * @param dataArea the area in which the data is to be plotted.
435      * @param plot the plot.
436      * @param rendererIndex the renderer index.
437      * @param info collects chart rendering information for return to caller.
438      *
439      * @return The renderer state.
440      */

441     public CategoryItemRendererState initialise(Graphics2D JavaDoc g2,
442                                                 Rectangle2D JavaDoc dataArea,
443                                                 CategoryPlot plot,
444                                                 int rendererIndex,
445                                                 PlotRenderingInfo info) {
446
447         CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
448                 rendererIndex, info);
449
450         // get the clipping values...
451
ValueAxis rangeAxis = getRangeAxis(plot, rendererIndex);
452         this.lowerClip = rangeAxis.getRange().getLowerBound();
453         this.upperClip = rangeAxis.getRange().getUpperBound();
454
455         // calculate the bar width
456
calculateBarWidth(plot, dataArea, rendererIndex, state);
457
458         return state;
459         
460     }
461     
462     /**
463      * Calculates the bar width and stores it in the renderer state.
464      *
465      * @param plot the plot.
466      * @param dataArea the data area.
467      * @param rendererIndex the renderer index.
468      * @param state the renderer state.
469      */

470     protected void calculateBarWidth(CategoryPlot plot,
471                                      Rectangle2D JavaDoc dataArea,
472                                      int rendererIndex,
473                                      CategoryItemRendererState state) {
474                                          
475         CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
476         CategoryDataset dataset = plot.getDataset(rendererIndex);
477         if (dataset != null) {
478             int columns = dataset.getColumnCount();
479             int rows = dataset.getRowCount();
480             double space = 0.0;
481             PlotOrientation orientation = plot.getOrientation();
482             if (orientation == PlotOrientation.HORIZONTAL) {
483                 space = dataArea.getHeight();
484             }
485             else if (orientation == PlotOrientation.VERTICAL) {
486                 space = dataArea.getWidth();
487             }
488             double maxWidth = space * getMaximumBarWidth();
489             double categoryMargin = 0.0;
490             double currentItemMargin = 0.0;
491             if (columns > 1) {
492                 categoryMargin = domainAxis.getCategoryMargin();
493             }
494             if (rows > 1) {
495                 currentItemMargin = getItemMargin();
496             }
497             double used = space * (1 - domainAxis.getLowerMargin()
498                                      - domainAxis.getUpperMargin()
499                                      - categoryMargin - currentItemMargin);
500             if ((rows * columns) > 0) {
501                 state.setBarWidth(Math.min(used / (rows * columns), maxWidth));
502             }
503             else {
504                 state.setBarWidth(Math.min(used, maxWidth));
505             }
506         }
507     }
508
509     /**
510      * Calculates the coordinate of the first "side" of a bar. This will be
511      * the minimum x-coordinate for a vertical bar, and the minimum
512      * y-coordinate for a horizontal bar.
513      *
514      * @param plot the plot.
515      * @param orientation the plot orientation.
516      * @param dataArea the data area.
517      * @param domainAxis the domain axis.
518      * @param state the renderer state (has the bar width precalculated).
519      * @param row the row index.
520      * @param column the column index.
521      *
522      * @return The coordinate.
523      */

524     protected double calculateBarW0(CategoryPlot plot,
525                                     PlotOrientation orientation,
526                                     Rectangle2D JavaDoc dataArea,
527                                     CategoryAxis domainAxis,
528                                     CategoryItemRendererState state,
529                                     int row,
530                                     int column) {
531         // calculate bar width...
532
double space = 0.0;
533         if (orientation == PlotOrientation.HORIZONTAL) {
534             space = dataArea.getHeight();
535         }
536         else {
537             space = dataArea.getWidth();
538         }
539         double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
540                 dataArea, plot.getDomainAxisEdge());
541         int seriesCount = getRowCount();
542         int categoryCount = getColumnCount();
543         if (seriesCount > 1) {
544             double seriesGap = space * getItemMargin()
545                                / (categoryCount * (seriesCount - 1));
546             double seriesW = calculateSeriesWidth(space, domainAxis,
547                     categoryCount, seriesCount);
548             barW0 = barW0 + row * (seriesW + seriesGap)
549                           + (seriesW / 2.0) - (state.getBarWidth() / 2.0);
550         }
551         else {
552             barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
553                     dataArea, plot.getDomainAxisEdge()) - state.getBarWidth()
554                     / 2.0;
555         }
556         return barW0;
557     }
558     
559     /**
560      * Calculates the coordinates for the length of a single bar.
561      *
562      * @param value the value represented by the bar.
563      *
564      * @return The coordinates for each end of the bar (or <code>null</code> if
565      * the bar is not visible for the current axis range).
566      */

567     protected double[] calculateBarL0L1(double value) {
568         double lclip = getLowerClip();
569         double uclip = getUpperClip();
570         double barLow = Math.min(this.base, value);
571         double barHigh = Math.max(this.base, value);
572         if (barHigh < lclip) { // bar is not visible
573
return null;
574         }
575         if (barLow > uclip) { // bar is not visible
576
return null;
577         }
578         barLow = Math.max(barLow, lclip);
579         barHigh = Math.min(barHigh, uclip);
580         return new double[] {barLow, barHigh};
581     }
582
583     /**
584      * Returns the range of values the renderer requires to display all the
585      * items from the specified dataset. This takes into account the range
586      * of values in the dataset, plus the flag that determines whether or not
587      * the base value for the bars should be included in the range.
588      *
589      * @param dataset the dataset (<code>null</code> permitted).
590      *
591      * @return The range (or <code>null</code> if the dataset is
592      * <code>null</code> or empty).
593      */

594     public Range findRangeBounds(CategoryDataset dataset) {
595         Range result = DatasetUtilities.findRangeBounds(dataset);
596         if (result != null) {
597             if (this.includeBaseInRange) {
598                 result = Range.expandToInclude(result, this.base);
599             }
600         }
601         return result;
602     }
603
604     /**
605      * Returns a legend item for a series.
606      *
607      * @param datasetIndex the dataset index (zero-based).
608      * @param series the series index (zero-based).
609      *
610      * @return The legend item.
611      */

612     public LegendItem getLegendItem(int datasetIndex, int series) {
613
614         CategoryPlot cp = getPlot();
615         if (cp == null) {
616             return null;
617         }
618
619         CategoryDataset dataset;
620         dataset = cp.getDataset(datasetIndex);
621         String JavaDoc label = getLegendItemLabelGenerator().generateLabel(dataset,
622                 series);
623         String JavaDoc description = label;
624         String JavaDoc toolTipText = null;
625         if (getLegendItemToolTipGenerator() != null) {
626             toolTipText = getLegendItemToolTipGenerator().generateLabel(
627                     dataset, series);
628         }
629         String JavaDoc urlText = null;
630         if (getLegendItemURLGenerator() != null) {
631             urlText = getLegendItemURLGenerator().generateLabel(dataset,
632                     series);
633         }
634         Shape JavaDoc shape = new Rectangle2D.Double JavaDoc(-4.0, -4.0, 8.0, 8.0);
635         Paint JavaDoc paint = getSeriesPaint(series);
636         Paint JavaDoc outlinePaint = getSeriesOutlinePaint(series);
637         Stroke JavaDoc outlineStroke = getSeriesOutlineStroke(series);
638
639         return new LegendItem(label, description, toolTipText, urlText,
640                       true, shape, true, paint,
641                       isDrawBarOutline(), outlinePaint, outlineStroke,
642                       false, new Line2D.Float JavaDoc(), new BasicStroke JavaDoc(1.0f),
643                       Color.black);
644     }
645
646     /**
647      * Draws the bar for a single (series, category) data item.
648      *
649      * @param g2 the graphics device.
650      * @param state the renderer state.
651      * @param dataArea the data area.
652      * @param plot the plot.
653      * @param domainAxis the domain axis.
654      * @param rangeAxis the range axis.
655      * @param dataset the dataset.
656      * @param row the row index (zero-based).
657      * @param column the column index (zero-based).
658      * @param pass the pass index.
659      */

660     public void drawItem(Graphics2D JavaDoc g2,
661                          CategoryItemRendererState state,
662                          Rectangle2D JavaDoc dataArea,
663                          CategoryPlot plot,
664                          CategoryAxis domainAxis,
665                          ValueAxis rangeAxis,
666                          CategoryDataset dataset,
667                          int row,
668                          int column,
669                          int pass) {
670
671         // nothing is drawn for null values...
672
Number JavaDoc dataValue = dataset.getValue(row, column);
673         if (dataValue == null) {
674             return;
675         }
676         
677         double value = dataValue.doubleValue();
678         
679         PlotOrientation orientation = plot.getOrientation();
680         double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis,
681                 state, row, column);
682         double[] barL0L1 = calculateBarL0L1(value);
683         if (barL0L1 == null) {
684             return; // the bar is not visible
685
}
686         
687         RectangleEdge edge = plot.getRangeAxisEdge();
688         double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge);
689         double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge);
690         double barL0 = Math.min(transL0, transL1);
691         double barLength = Math.max(Math.abs(transL1 - transL0),
692                 getMinimumBarLength());
693
694         // draw the bar...
695
Rectangle2D JavaDoc bar = null;
696         if (orientation == PlotOrientation.HORIZONTAL) {
697             bar = new Rectangle2D.Double JavaDoc(barL0, barW0, barLength,
698                     state.getBarWidth());
699         }
700         else {
701             bar = new Rectangle2D.Double JavaDoc(barW0, barL0, state.getBarWidth(),
702                     barLength);
703         }
704         Paint JavaDoc itemPaint = getItemPaint(row, column);
705         GradientPaintTransformer t = getGradientPaintTransformer();
706         if (t != null && itemPaint instanceof GradientPaint JavaDoc) {
707             itemPaint = t.transform((GradientPaint JavaDoc) itemPaint, bar);
708         }
709         g2.setPaint(itemPaint);
710         g2.fill(bar);
711
712         // draw the outline...
713
if (isDrawBarOutline()
714                 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
715             Stroke JavaDoc stroke = getItemOutlineStroke(row, column);
716             Paint JavaDoc paint = getItemOutlinePaint(row, column);
717             if (stroke != null && paint != null) {
718                 g2.setStroke(stroke);
719                 g2.setPaint(paint);
720                 g2.draw(bar);
721             }
722         }
723
724         CategoryItemLabelGenerator generator
725             = getItemLabelGenerator(row, column);
726         if (generator != null && isItemLabelVisible(row, column)) {
727             drawItemLabel(g2, dataset, row, column, plot, generator, bar,
728                     (value < 0.0));
729         }
730
731         // add an item entity, if this information is being collected
732
EntityCollection entities = state.getEntityCollection();
733         if (entities != null) {
734             addItemEntity(entities, dataset, row, column, bar);
735         }
736
737     }
738
739     /**
740      * Calculates the available space for each series.
741      *
742      * @param space the space along the entire axis (in Java2D units).
743      * @param axis the category axis.
744      * @param categories the number of categories.
745      * @param series the number of series.
746      *
747      * @return The width of one series.
748      */

749     protected double calculateSeriesWidth(double space, CategoryAxis axis,
750                                           int categories, int series) {
751         double factor = 1.0 - getItemMargin() - axis.getLowerMargin()
752                             - axis.getUpperMargin();
753         if (categories > 1) {
754             factor = factor - axis.getCategoryMargin();
755         }
756         return (space * factor) / (categories * series);
757     }
758     
759     /**
760      * Draws an item label. This method is overridden so that the bar can be
761      * used to calculate the label anchor point.
762      *
763      * @param g2 the graphics device.
764      * @param data the dataset.
765      * @param row the row.
766      * @param column the column.
767      * @param plot the plot.
768      * @param generator the label generator.
769      * @param bar the bar.
770      * @param negative a flag indicating a negative value.
771      */

772     protected void drawItemLabel(Graphics2D JavaDoc g2,
773                                  CategoryDataset data,
774                                  int row,
775                                  int column,
776                                  CategoryPlot plot,
777                                  CategoryItemLabelGenerator generator,
778                                  Rectangle2D JavaDoc bar,
779                                  boolean negative) {
780                                      
781         String JavaDoc label = generator.generateLabel(data, row, column);
782         if (label == null) {
783             return; // nothing to do
784
}
785         
786         Font JavaDoc labelFont = getItemLabelFont(row, column);
787         g2.setFont(labelFont);
788         Paint JavaDoc paint = getItemLabelPaint(row, column);
789         g2.setPaint(paint);
790
791         // find out where to place the label...
792
ItemLabelPosition position = null;
793         if (!negative) {
794             position = getPositiveItemLabelPosition(row, column);
795         }
796         else {
797             position = getNegativeItemLabelPosition(row, column);
798         }
799
800         // work out the label anchor point...
801
Point2D JavaDoc anchorPoint = calculateLabelAnchorPoint(
802                 position.getItemLabelAnchor(), bar, plot.getOrientation());
803         
804         if (isInternalAnchor(position.getItemLabelAnchor())) {
805             Shape JavaDoc bounds = TextUtilities.calculateRotatedStringBounds(label,
806                     g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
807                     position.getTextAnchor(), position.getAngle(),
808                     position.getRotationAnchor());
809             
810             if (bounds != null) {
811                 if (!bar.contains(bounds.getBounds2D())) {
812                     if (!negative) {
813                         position = getPositiveItemLabelPositionFallback();
814                     }
815                     else {
816                         position = getNegativeItemLabelPositionFallback();
817                     }
818                     if (position != null) {
819                         anchorPoint = calculateLabelAnchorPoint(
820                                 position.getItemLabelAnchor(), bar,
821                                 plot.getOrientation());
822                     }
823                 }
824             }
825         
826         }
827         
828         if (position != null) {
829             TextUtilities.drawRotatedString(label, g2,
830                     (float) anchorPoint.getX(), (float) anchorPoint.getY(),
831                     position.getTextAnchor(), position.getAngle(),
832                     position.getRotationAnchor());
833         }
834     }
835     
836     /**
837      * Calculates the item label anchor point.
838      *
839      * @param anchor the anchor.
840      * @param bar the bar.
841      * @param orientation the plot orientation.
842      *
843      * @return The anchor point.
844      */

845     private Point2D JavaDoc calculateLabelAnchorPoint(ItemLabelAnchor anchor,
846                                               Rectangle2D JavaDoc bar,
847                                               PlotOrientation orientation) {
848
849         Point2D JavaDoc result = null;
850         double offset = getItemLabelAnchorOffset();
851         double x0 = bar.getX() - offset;
852         double x1 = bar.getX();
853         double x2 = bar.getX() + offset;
854         double x3 = bar.getCenterX();
855         double x4 = bar.getMaxX() - offset;
856         double x5 = bar.getMaxX();
857         double x6 = bar.getMaxX() + offset;
858
859         double y0 = bar.getMaxY() + offset;
860         double y1 = bar.getMaxY();
861         double y2 = bar.getMaxY() - offset;
862         double y3 = bar.getCenterY();
863         double y4 = bar.getMinY() + offset;
864         double y5 = bar.getMinY();
865         double y6 = bar.getMinY() - offset;
866
867         if (anchor == ItemLabelAnchor.CENTER) {
868             result = new Point2D.Double JavaDoc(x3, y3);
869         }
870         else if (anchor == ItemLabelAnchor.INSIDE1) {
871             result = new Point2D.Double JavaDoc(x4, y4);
872         }
873         else if (anchor == ItemLabelAnchor.INSIDE2) {
874             result = new Point2D.Double JavaDoc(x4, y4);
875         }
876         else if (anchor == ItemLabelAnchor.INSIDE3) {
877             result = new Point2D.Double JavaDoc(x4, y3);
878         }
879         else if (anchor == ItemLabelAnchor.INSIDE4) {
880             result = new Point2D.Double JavaDoc(x4, y2);
881         }
882         else if (anchor == ItemLabelAnchor.INSIDE5) {
883             result = new Point2D.Double JavaDoc(x4, y2);
884         }
885         else if (anchor == ItemLabelAnchor.INSIDE6) {
886             result = new Point2D.Double JavaDoc(x3, y2);
887         }
888         else if (anchor == ItemLabelAnchor.INSIDE7) {
889             result = new Point2D.Double JavaDoc(x2, y2);
890         }
891         else if (anchor == ItemLabelAnchor.INSIDE8) {
892             result = new Point2D.Double JavaDoc(x2, y2);
893         }
894         else if (anchor == ItemLabelAnchor.INSIDE9) {
895             result = new Point2D.Double JavaDoc(x2, y3);
896         }
897         else if (anchor == ItemLabelAnchor.INSIDE10) {
898             result = new Point2D.Double JavaDoc(x2, y4);
899         }
900         else if (anchor == ItemLabelAnchor.INSIDE11) {
901             result = new Point2D.Double JavaDoc(x2, y4);
902         }
903         else if (anchor == ItemLabelAnchor.INSIDE12) {
904             result = new Point2D.Double JavaDoc(x3, y4);
905         }
906         else if (anchor == ItemLabelAnchor.OUTSIDE1) {
907             result = new Point2D.Double JavaDoc(x5, y6);
908         }
909         else if (anchor == ItemLabelAnchor.OUTSIDE2) {
910             result = new Point2D.Double JavaDoc(x6, y5);
911         }
912         else if (anchor == ItemLabelAnchor.OUTSIDE3) {
913             result = new Point2D.Double JavaDoc(x6, y3);
914         }
915         else if (anchor == ItemLabelAnchor.OUTSIDE4) {
916             result = new Point2D.Double JavaDoc(x6, y1);
917         }
918         else if (anchor == ItemLabelAnchor.OUTSIDE5) {
919             result = new Point2D.Double JavaDoc(x5, y0);
920         }
921         else if (anchor == ItemLabelAnchor.OUTSIDE6) {
922             result = new Point2D.Double JavaDoc(x3, y0);
923         }
924         else if (anchor == ItemLabelAnchor.OUTSIDE7) {
925             result = new Point2D.Double JavaDoc(x1, y0);
926         }
927         else if (anchor == ItemLabelAnchor.OUTSIDE8) {
928             result = new Point2D.Double JavaDoc(x0, y1);
929         }
930         else if (anchor == ItemLabelAnchor.OUTSIDE9) {
931             result = new Point2D.Double JavaDoc(x0, y3);
932         }
933         else if (anchor == ItemLabelAnchor.OUTSIDE10) {
934             result = new Point2D.Double JavaDoc(x0, y5);
935         }
936         else if (anchor == ItemLabelAnchor.OUTSIDE11) {
937             result = new Point2D.Double JavaDoc(x1, y6);
938         }
939         else if (anchor == ItemLabelAnchor.OUTSIDE12) {
940             result = new Point2D.Double JavaDoc(x3, y6);
941         }
942
943         return result;
944
945     }
946     
947     /**
948      * Returns <code>true</code> if the specified anchor point is inside a bar.
949      *
950      * @param anchor the anchor point.
951      *
952      * @return A boolean.
953      */

954     private boolean isInternalAnchor(ItemLabelAnchor anchor) {
955         return anchor == ItemLabelAnchor.CENTER
956                || anchor == ItemLabelAnchor.INSIDE1
957                || anchor == ItemLabelAnchor.INSIDE2
958                || anchor == ItemLabelAnchor.INSIDE3
959                || anchor == ItemLabelAnchor.INSIDE4
960                || anchor == ItemLabelAnchor.INSIDE5
961                || anchor == ItemLabelAnchor.INSIDE6
962                || anchor == ItemLabelAnchor.INSIDE7
963                || anchor == ItemLabelAnchor.INSIDE8
964                || anchor == ItemLabelAnchor.INSIDE9
965                || anchor == ItemLabelAnchor.INSIDE10
966                || anchor == ItemLabelAnchor.INSIDE11
967                || anchor == ItemLabelAnchor.INSIDE12;
968     }
969     
970     /**
971      * Tests this instance for equality with an arbitrary object.
972      *
973      * @param obj the object (<code>null</code> permitted).
974      *
975      * @return A boolean.
976      */

977     public boolean equals(Object JavaDoc obj) {
978         
979         if (obj == this) {
980             return true;
981         }
982         if (!(obj instanceof BarRenderer)) {
983             return false;
984         }
985         if (!super.equals(obj)) {
986             return false;
987         }
988         BarRenderer that = (BarRenderer) obj;
989         if (this.base != that.base) {
990             return false;
991         }
992         if (this.itemMargin != that.itemMargin) {
993             return false;
994         }
995         if (this.drawBarOutline != that.drawBarOutline) {
996             return false;
997         }
998         if (this.maximumBarWidth != that.maximumBarWidth) {
999             return false;
1000        }
1001        if (this.minimumBarLength != that.minimumBarLength) {
1002            return false;
1003        }
1004        if (!ObjectUtilities.equal(this.gradientPaintTransformer,
1005                that.gradientPaintTransformer)) {
1006            return false;
1007        }
1008        if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback,
1009            that.positiveItemLabelPositionFallback)) {
1010            return false;
1011        }
1012        if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback,
1013            that.negativeItemLabelPositionFallback)) {
1014            return false;
1015        }
1016        return true;
1017        
1018    }
1019
1020}
1021
Popular Tags