KickJava   Java API By Example, From Geeks To Geeks.

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


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  * StackedBarRenderer.java
29  * -----------------------
30  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): Richard Atkinson;
34  * Thierry Saura;
35  * Christian W. Zuckschwerdt;
36  *
37  * $Id: StackedBarRenderer.java,v 1.10.2.8 2006/10/11 09:41:49 mungady Exp $
38  *
39  * Changes
40  * -------
41  * 19-Oct-2001 : Version 1 (DG);
42  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
43  * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
44  * available space rather than a fixed number of units (DG);
45  * 15-Nov-2001 : Modified to allow for null data values (DG);
46  * 22-Nov-2001 : Modified to allow for negative data values (DG);
47  * 13-Dec-2001 : Added tooltips (DG);
48  * 16-Jan-2002 : Fixed bug for single category datasets (DG);
49  * 15-Feb-2002 : Added isStacked() method (DG);
50  * 14-Mar-2002 : Modified to implement the CategoryItemRenderer interface (DG);
51  * 24-May-2002 : Incorporated tooltips into chart entities (DG);
52  * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix
53  * reported by David Basten. Also updated Javadocs. (DG);
54  * 25-Jun-2002 : Removed redundant import (DG);
55  * 26-Jun-2002 : Small change to entity (DG);
56  * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
57  * for HTML image maps (RA);
58  * 08-Aug-2002 : Added optional linking lines, contributed by Thierry
59  * Saura (DG);
60  * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
61  * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
62  * CategoryToolTipGenerator interface (DG);
63  * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
64  * 26-Nov-2002 : Replaced isStacked() method with getRangeType() method (DG);
65  * 17-Jan-2003 : Moved plot classes to a separate package (DG);
66  * 25-Mar-2003 : Implemented Serializable (DG);
67  * 12-May-2003 : Merged horizontal and vertical stacked bar renderers (DG);
68  * 30-Jul-2003 : Modified entity constructor (CZ);
69  * 08-Sep-2003 : Fixed bug 799668 (isBarOutlineDrawn() ignored) (DG);
70  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
71  * 21-Oct-2003 : Moved bar width into renderer state (DG);
72  * 26-Nov-2003 : Added code to respect maxBarWidth attribute (DG);
73  * 05-Nov-2004 : Changed to a two-pass renderer so that item labels are not
74  * overwritten by other bars (DG);
75  * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG);
76  * 29-Mar-2005 : Modified drawItem() method so that a zero value is handled
77  * within the code for positive rather than negative values (DG);
78  * 20-Apr-2005 : Renamed CategoryLabelGenerator
79  * --> CategoryItemLabelGenerator (DG);
80  * 17-May-2005 : Added flag to allow rendering values as percentages - inspired
81  * by patch 1200886 submitted by John Xiao (DG);
82  * 09-Jun-2005 : Added accessor methods for the renderAsPercentages flag,
83  * provided equals() method, and use addItemEntity from
84  * superclass (DG);
85  * 09-Jun-2005 : Added support for GradientPaint - see bug report 1215670 (DG);
86  * 22-Sep-2005 : Renamed getMaxBarWidth() --> getMaximumBarWidth() (DG);
87  * 29-Sep-2005 : Use outline stroke in drawItem method - see bug report
88  * 1304139 (DG);
89  * ------------- JFREECHART 1.0.x ---------------------------------------------
90  * 11-Oct-2006 : Source reformatting (DG);
91  *
92  */

93
94 package org.jfree.chart.renderer.category;
95
96 import java.awt.GradientPaint JavaDoc;
97 import java.awt.Graphics2D JavaDoc;
98 import java.awt.Paint JavaDoc;
99 import java.awt.geom.Rectangle2D JavaDoc;
100 import java.io.Serializable JavaDoc;
101
102 import org.jfree.chart.axis.CategoryAxis;
103 import org.jfree.chart.axis.ValueAxis;
104 import org.jfree.chart.entity.EntityCollection;
105 import org.jfree.chart.event.RendererChangeEvent;
106 import org.jfree.chart.labels.CategoryItemLabelGenerator;
107 import org.jfree.chart.labels.ItemLabelAnchor;
108 import org.jfree.chart.labels.ItemLabelPosition;
109 import org.jfree.chart.plot.CategoryPlot;
110 import org.jfree.chart.plot.PlotOrientation;
111 import org.jfree.data.DataUtilities;
112 import org.jfree.data.Range;
113 import org.jfree.data.category.CategoryDataset;
114 import org.jfree.data.general.DatasetUtilities;
115 import org.jfree.ui.GradientPaintTransformer;
116 import org.jfree.ui.RectangleEdge;
117 import org.jfree.ui.TextAnchor;
118 import org.jfree.util.PublicCloneable;
119
120 /**
121  * A stacked bar renderer for use with the
122  * {@link org.jfree.chart.plot.CategoryPlot} class.
123  */

124 public class StackedBarRenderer extends BarRenderer
125                                 implements Cloneable JavaDoc, PublicCloneable,
126                                            Serializable JavaDoc {
127
128     /** For serialization. */
129     static final long serialVersionUID = 6402943811500067531L;
130     
131     /** A flag that controls whether the bars display values or percentages. */
132     private boolean renderAsPercentages;
133     
134     /**
135      * Creates a new renderer. By default, the renderer has no tool tip
136      * generator and no URL generator. These defaults have been chosen to
137      * minimise the processing required to generate a default chart. If you
138      * require tool tips or URLs, then you can easily add the required
139      * generators.
140      */

141     public StackedBarRenderer() {
142         this(false);
143     }
144     
145     /**
146      * Creates a new renderer.
147      *
148      * @param renderAsPercentages a flag that controls whether the data values
149      * are rendered as percentages.
150      */

151     public StackedBarRenderer(boolean renderAsPercentages) {
152         super();
153         this.renderAsPercentages = renderAsPercentages;
154         
155         // set the default item label positions, which will only be used if
156
// the user requests visible item labels...
157
ItemLabelPosition p = new ItemLabelPosition(ItemLabelAnchor.CENTER,
158                 TextAnchor.CENTER);
159         setBasePositiveItemLabelPosition(p);
160         setBaseNegativeItemLabelPosition(p);
161         setPositiveItemLabelPositionFallback(null);
162         setNegativeItemLabelPositionFallback(null);
163     }
164
165     /**
166      * Returns <code>true</code> if the renderer displays each item value as
167      * a percentage (so that the stacked bars add to 100%), and
168      * <code>false</code> otherwise.
169      *
170      * @return A boolean.
171      */

172     public boolean getRenderAsPercentages() {
173         return this.renderAsPercentages;
174     }
175     
176     /**
177      * Sets the flag that controls whether the renderer displays each item
178      * value as a percentage (so that the stacked bars add to 100%), and sends
179      * a {@link RendererChangeEvent} to all registered listeners.
180      *
181      * @param asPercentages the flag.
182      */

183     public void setRenderAsPercentages(boolean asPercentages) {
184         this.renderAsPercentages = asPercentages;
185         notifyListeners(new RendererChangeEvent(this));
186     }
187     
188     /**
189      * Returns the number of passes (<code>2</code>) required by this renderer.
190      * The first pass is used to draw the bars, the second pass is used to
191      * draw the item labels (if visible).
192      *
193      * @return The number of passes required by the renderer.
194      */

195     public int getPassCount() {
196         return 2;
197     }
198     
199     /**
200      * Returns the range of values the renderer requires to display all the
201      * items from the specified dataset.
202      *
203      * @param dataset the dataset (<code>null</code> permitted).
204      *
205      * @return The range (or <code>null</code> if the dataset is empty).
206      */

207     public Range findRangeBounds(CategoryDataset dataset) {
208         if (this.renderAsPercentages) {
209             return new Range(0.0, 1.0);
210         }
211         else {
212             return DatasetUtilities.findStackedRangeBounds(dataset, getBase());
213         }
214     }
215
216     /**
217      * Calculates the bar width and stores it in the renderer state.
218      *
219      * @param plot the plot.
220      * @param dataArea the data area.
221      * @param rendererIndex the renderer index.
222      * @param state the renderer state.
223      */

224     protected void calculateBarWidth(CategoryPlot plot,
225                                      Rectangle2D JavaDoc dataArea,
226                                      int rendererIndex,
227                                      CategoryItemRendererState state) {
228
229         // calculate the bar width
230
CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex);
231         CategoryDataset data = plot.getDataset(rendererIndex);
232         if (data != null) {
233             PlotOrientation orientation = plot.getOrientation();
234             double space = 0.0;
235             if (orientation == PlotOrientation.HORIZONTAL) {
236                 space = dataArea.getHeight();
237             }
238             else if (orientation == PlotOrientation.VERTICAL) {
239                 space = dataArea.getWidth();
240             }
241             double maxWidth = space * getMaximumBarWidth();
242             int columns = data.getColumnCount();
243             double categoryMargin = 0.0;
244             if (columns > 1) {
245                 categoryMargin = xAxis.getCategoryMargin();
246             }
247
248             double used = space * (1 - xAxis.getLowerMargin()
249                                      - xAxis.getUpperMargin()
250                                      - categoryMargin);
251             if (columns > 0) {
252                 state.setBarWidth(Math.min(used / columns, maxWidth));
253             }
254             else {
255                 state.setBarWidth(Math.min(used, maxWidth));
256             }
257         }
258
259     }
260
261     /**
262      * Draws a stacked bar for a specific item.
263      *
264      * @param g2 the graphics device.
265      * @param state the renderer state.
266      * @param dataArea the plot area.
267      * @param plot the plot.
268      * @param domainAxis the domain (category) axis.
269      * @param rangeAxis the range (value) axis.
270      * @param dataset the data.
271      * @param row the row index (zero-based).
272      * @param column the column index (zero-based).
273      * @param pass the pass index.
274      */

275     public void drawItem(Graphics2D JavaDoc g2,
276                          CategoryItemRendererState state,
277                          Rectangle2D JavaDoc dataArea,
278                          CategoryPlot plot,
279                          CategoryAxis domainAxis,
280                          ValueAxis rangeAxis,
281                          CategoryDataset dataset,
282                          int row,
283                          int column,
284                          int pass) {
285      
286         // nothing is drawn for null values...
287
Number JavaDoc dataValue = dataset.getValue(row, column);
288         if (dataValue == null) {
289             return;
290         }
291         
292         double value = dataValue.doubleValue();
293         double total = 0.0; // only needed if calculating percentages
294
if (this.renderAsPercentages) {
295             total = DataUtilities.calculateColumnTotal(dataset, column);
296             value = value / total;
297         }
298         
299         PlotOrientation orientation = plot.getOrientation();
300         double barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
301                 dataArea, plot.getDomainAxisEdge())
302                 - state.getBarWidth() / 2.0;
303
304         double positiveBase = getBase();
305         double negativeBase = positiveBase;
306
307         for (int i = 0; i < row; i++) {
308             Number JavaDoc v = dataset.getValue(i, column);
309             if (v != null) {
310                 double d = v.doubleValue();
311                 if (this.renderAsPercentages) {
312                     d = d / total;
313                 }
314                 if (d > 0) {
315                     positiveBase = positiveBase + d;
316                 }
317                 else {
318                     negativeBase = negativeBase + d;
319                 }
320             }
321         }
322
323         double translatedBase;
324         double translatedValue;
325         RectangleEdge location = plot.getRangeAxisEdge();
326         if (value >= 0.0) {
327             translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea,
328                     location);
329             translatedValue = rangeAxis.valueToJava2D(positiveBase + value,
330                     dataArea, location);
331         }
332         else {
333             translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea,
334                     location);
335             translatedValue = rangeAxis.valueToJava2D(negativeBase + value,
336                     dataArea, location);
337         }
338         double barL0 = Math.min(translatedBase, translatedValue);
339         double barLength = Math.max(Math.abs(translatedValue - translatedBase),
340                 getMinimumBarLength());
341
342         Rectangle2D JavaDoc bar = null;
343         if (orientation == PlotOrientation.HORIZONTAL) {
344             bar = new Rectangle2D.Double JavaDoc(barL0, barW0, barLength,
345                     state.getBarWidth());
346         }
347         else {
348             bar = new Rectangle2D.Double JavaDoc(barW0, barL0, state.getBarWidth(),
349                     barLength);
350         }
351         if (pass == 0) {
352             Paint JavaDoc itemPaint = getItemPaint(row, column);
353             GradientPaintTransformer t = getGradientPaintTransformer();
354             if (t != null && itemPaint instanceof GradientPaint JavaDoc) {
355                 itemPaint = t.transform((GradientPaint JavaDoc) itemPaint, bar);
356             }
357             g2.setPaint(itemPaint);
358             g2.fill(bar);
359             if (isDrawBarOutline()
360                     && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
361                 g2.setStroke(getItemOutlineStroke(row, column));
362                 g2.setPaint(getItemOutlinePaint(row, column));
363                 g2.draw(bar);
364             }
365
366             // add an item entity, if this information is being collected
367
EntityCollection entities = state.getEntityCollection();
368             if (entities != null) {
369                 addItemEntity(entities, dataset, row, column, bar);
370             }
371         }
372         else if (pass == 1) {
373             CategoryItemLabelGenerator generator
374                 = getItemLabelGenerator(row, column);
375             if (generator != null && isItemLabelVisible(row, column)) {
376                 drawItemLabel(g2, dataset, row, column, plot, generator, bar,
377                         (value < 0.0));
378             }
379         }
380     }
381
382     /**
383      * Tests this renderer for equality with an arbitrary object.
384      *
385      * @param obj the object (<code>null</code> permitted).
386      *
387      * @return A boolean.
388      */

389     public boolean equals(Object JavaDoc obj) {
390         if (obj == this) {
391             return true;
392         }
393         if (!(obj instanceof StackedBarRenderer)) {
394             return false;
395         }
396         if (!super.equals(obj)) {
397             return false;
398         }
399         StackedBarRenderer that = (StackedBarRenderer) obj;
400         if (this.renderAsPercentages != that.renderAsPercentages) {
401             return false;
402         }
403         return true;
404     }
405
406 }
407
Popular Tags