Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » category » [javadoc | source]
    1   /* ===========================================================
    2    * JFreeChart : a free chart library for the Java(tm) platform
    3    * ===========================================================
    4    *
    5    * (C) Copyright 2000-2008, 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-2008, 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    * Changes
   38    * -------
   39    * 19-Oct-2001 : Version 1 (DG);
   40    * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
   41    * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
   42    *               available space rather than a fixed number of units (DG);
   43    * 15-Nov-2001 : Modified to allow for null data values (DG);
   44    * 22-Nov-2001 : Modified to allow for negative data values (DG);
   45    * 13-Dec-2001 : Added tooltips (DG);
   46    * 16-Jan-2002 : Fixed bug for single category datasets (DG);
   47    * 15-Feb-2002 : Added isStacked() method (DG);
   48    * 14-Mar-2002 : Modified to implement the CategoryItemRenderer interface (DG);
   49    * 24-May-2002 : Incorporated tooltips into chart entities (DG);
   50    * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix
   51    *               reported by David Basten.  Also updated Javadocs. (DG);
   52    * 25-Jun-2002 : Removed redundant import (DG);
   53    * 26-Jun-2002 : Small change to entity (DG);
   54    * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
   55    *               for HTML image maps (RA);
   56    * 08-Aug-2002 : Added optional linking lines, contributed by Thierry
   57    *               Saura (DG);
   58    * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
   59    * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
   60    *               CategoryToolTipGenerator interface (DG);
   61    * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
   62    * 26-Nov-2002 : Replaced isStacked() method with getRangeType() method (DG);
   63    * 17-Jan-2003 : Moved plot classes to a separate package (DG);
   64    * 25-Mar-2003 : Implemented Serializable (DG);
   65    * 12-May-2003 : Merged horizontal and vertical stacked bar renderers (DG);
   66    * 30-Jul-2003 : Modified entity constructor (CZ);
   67    * 08-Sep-2003 : Fixed bug 799668 (isBarOutlineDrawn() ignored) (DG);
   68    * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
   69    * 21-Oct-2003 : Moved bar width into renderer state (DG);
   70    * 26-Nov-2003 : Added code to respect maxBarWidth attribute (DG);
   71    * 05-Nov-2004 : Changed to a two-pass renderer so that item labels are not
   72    *               overwritten by other bars (DG);
   73    * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG);
   74    * 29-Mar-2005 : Modified drawItem() method so that a zero value is handled
   75    *               within the code for positive rather than negative values (DG);
   76    * 20-Apr-2005 : Renamed CategoryLabelGenerator
   77    *               --> CategoryItemLabelGenerator (DG);
   78    * 17-May-2005 : Added flag to allow rendering values as percentages - inspired
   79    *               by patch 1200886 submitted by John Xiao (DG);
   80    * 09-Jun-2005 : Added accessor methods for the renderAsPercentages flag,
   81    *               provided equals() method, and use addItemEntity from
   82    *               superclass (DG);
   83    * 09-Jun-2005 : Added support for GradientPaint - see bug report 1215670 (DG);
   84    * 22-Sep-2005 : Renamed getMaxBarWidth() --> getMaximumBarWidth() (DG);
   85    * 29-Sep-2005 : Use outline stroke in drawItem method - see bug report
   86    *               1304139 (DG);
   87    * ------------- JFREECHART 1.0.x ---------------------------------------------
   88    * 11-Oct-2006 : Source reformatting (DG);
   89    *
   90    */
   91   
   92   package org.jfree.chart.renderer.category;
   93   
   94   import java.awt.GradientPaint;
   95   import java.awt.Graphics2D;
   96   import java.awt.Paint;
   97   import java.awt.geom.Rectangle2D;
   98   import java.io.Serializable;
   99   
  100   import org.jfree.chart.axis.CategoryAxis;
  101   import org.jfree.chart.axis.ValueAxis;
  102   import org.jfree.chart.entity.EntityCollection;
  103   import org.jfree.chart.event.RendererChangeEvent;
  104   import org.jfree.chart.labels.CategoryItemLabelGenerator;
  105   import org.jfree.chart.labels.ItemLabelAnchor;
  106   import org.jfree.chart.labels.ItemLabelPosition;
  107   import org.jfree.chart.plot.CategoryPlot;
  108   import org.jfree.chart.plot.PlotOrientation;
  109   import org.jfree.data.DataUtilities;
  110   import org.jfree.data.Range;
  111   import org.jfree.data.category.CategoryDataset;
  112   import org.jfree.data.general.DatasetUtilities;
  113   import org.jfree.ui.GradientPaintTransformer;
  114   import org.jfree.ui.RectangleEdge;
  115   import org.jfree.ui.TextAnchor;
  116   import org.jfree.util.PublicCloneable;
  117   
  118   /**
  119    * A stacked bar renderer for use with the
  120    * {@link org.jfree.chart.plot.CategoryPlot} class.
  121    */
  122   public class StackedBarRenderer extends BarRenderer
  123           implements Cloneable, PublicCloneable, Serializable {
  124   
  125       /** For serialization. */
  126       static final long serialVersionUID = 6402943811500067531L;
  127   
  128       /** A flag that controls whether the bars display values or percentages. */
  129       private boolean renderAsPercentages;
  130   
  131       /**
  132        * Creates a new renderer.  By default, the renderer has no tool tip
  133        * generator and no URL generator.  These defaults have been chosen to
  134        * minimise the processing required to generate a default chart.  If you
  135        * require tool tips or URLs, then you can easily add the required
  136        * generators.
  137        */
  138       public StackedBarRenderer() {
  139           this(false);
  140       }
  141   
  142       /**
  143        * Creates a new renderer.
  144        *
  145        * @param renderAsPercentages  a flag that controls whether the data values
  146        *                             are rendered as percentages.
  147        */
  148       public StackedBarRenderer(boolean renderAsPercentages) {
  149           super();
  150           this.renderAsPercentages = renderAsPercentages;
  151   
  152           // set the default item label positions, which will only be used if
  153           // the user requests visible item labels...
  154           ItemLabelPosition p = new ItemLabelPosition(ItemLabelAnchor.CENTER,
  155                   TextAnchor.CENTER);
  156           setBasePositiveItemLabelPosition(p);
  157           setBaseNegativeItemLabelPosition(p);
  158           setPositiveItemLabelPositionFallback(null);
  159           setNegativeItemLabelPositionFallback(null);
  160       }
  161   
  162       /**
  163        * Returns <code>true</code> if the renderer displays each item value as
  164        * a percentage (so that the stacked bars add to 100%), and
  165        * <code>false</code> otherwise.
  166        *
  167        * @return A boolean.
  168        *
  169        * @see #setRenderAsPercentages(boolean)
  170        */
  171       public boolean getRenderAsPercentages() {
  172           return this.renderAsPercentages;
  173       }
  174   
  175       /**
  176        * Sets the flag that controls whether the renderer displays each item
  177        * value as a percentage (so that the stacked bars add to 100%), and sends
  178        * a {@link RendererChangeEvent} to all registered listeners.
  179        *
  180        * @param asPercentages  the flag.
  181        *
  182        * @see #getRenderAsPercentages()
  183        */
  184       public void setRenderAsPercentages(boolean asPercentages) {
  185           this.renderAsPercentages = asPercentages;
  186           fireChangeEvent();
  187       }
  188   
  189       /**
  190        * Returns the number of passes (<code>2</code>) required by this renderer.
  191        * The first pass is used to draw the bars, the second pass is used to
  192        * draw the item labels (if visible).
  193        *
  194        * @return The number of passes required by the renderer.
  195        */
  196       public int getPassCount() {
  197           return 2;
  198       }
  199   
  200       /**
  201        * Returns the range of values the renderer requires to display all the
  202        * items from the specified dataset.
  203        *
  204        * @param dataset  the dataset (<code>null</code> permitted).
  205        *
  206        * @return The range (or <code>null</code> if the dataset is empty).
  207        */
  208       public Range findRangeBounds(CategoryDataset dataset) {
  209           if (this.renderAsPercentages) {
  210               return new Range(0.0, 1.0);
  211           }
  212           else {
  213               return DatasetUtilities.findStackedRangeBounds(dataset, getBase());
  214           }
  215       }
  216   
  217       /**
  218        * Calculates the bar width and stores it in the renderer state.
  219        *
  220        * @param plot  the plot.
  221        * @param dataArea  the data area.
  222        * @param rendererIndex  the renderer index.
  223        * @param state  the renderer state.
  224        */
  225       protected void calculateBarWidth(CategoryPlot plot,
  226                                        Rectangle2D dataArea,
  227                                        int rendererIndex,
  228                                        CategoryItemRendererState state) {
  229   
  230           // calculate the bar width
  231           CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex);
  232           CategoryDataset data = plot.getDataset(rendererIndex);
  233           if (data != null) {
  234               PlotOrientation orientation = plot.getOrientation();
  235               double space = 0.0;
  236               if (orientation == PlotOrientation.HORIZONTAL) {
  237                   space = dataArea.getHeight();
  238               }
  239               else if (orientation == PlotOrientation.VERTICAL) {
  240                   space = dataArea.getWidth();
  241               }
  242               double maxWidth = space * getMaximumBarWidth();
  243               int columns = data.getColumnCount();
  244               double categoryMargin = 0.0;
  245               if (columns > 1) {
  246                   categoryMargin = xAxis.getCategoryMargin();
  247               }
  248   
  249               double used = space * (1 - xAxis.getLowerMargin()
  250                                        - xAxis.getUpperMargin()
  251                                        - categoryMargin);
  252               if (columns > 0) {
  253                   state.setBarWidth(Math.min(used / columns, maxWidth));
  254               }
  255               else {
  256                   state.setBarWidth(Math.min(used, maxWidth));
  257               }
  258           }
  259   
  260       }
  261   
  262       /**
  263        * Draws a stacked bar for a specific item.
  264        *
  265        * @param g2  the graphics device.
  266        * @param state  the renderer state.
  267        * @param dataArea  the plot area.
  268        * @param plot  the plot.
  269        * @param domainAxis  the domain (category) axis.
  270        * @param rangeAxis  the range (value) axis.
  271        * @param dataset  the data.
  272        * @param row  the row index (zero-based).
  273        * @param column  the column index (zero-based).
  274        * @param pass  the pass index.
  275        */
  276       public void drawItem(Graphics2D g2,
  277                            CategoryItemRendererState state,
  278                            Rectangle2D dataArea,
  279                            CategoryPlot plot,
  280                            CategoryAxis domainAxis,
  281                            ValueAxis rangeAxis,
  282                            CategoryDataset dataset,
  283                            int row,
  284                            int column,
  285                            int pass) {
  286   
  287           // nothing is drawn for null values...
  288           Number dataValue = dataset.getValue(row, column);
  289           if (dataValue == null) {
  290               return;
  291           }
  292   
  293           double value = dataValue.doubleValue();
  294           double total = 0.0;  // only needed if calculating percentages
  295           if (this.renderAsPercentages) {
  296               total = DataUtilities.calculateColumnTotal(dataset, column);
  297               value = value / total;
  298           }
  299   
  300           PlotOrientation orientation = plot.getOrientation();
  301           double barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
  302                   dataArea, plot.getDomainAxisEdge())
  303                   - state.getBarWidth() / 2.0;
  304   
  305           double positiveBase = getBase();
  306           double negativeBase = positiveBase;
  307   
  308           for (int i = 0; i < row; i++) {
  309               Number v = dataset.getValue(i, column);
  310               if (v != null) {
  311                   double d = v.doubleValue();
  312                   if (this.renderAsPercentages) {
  313                       d = d / total;
  314                   }
  315                   if (d > 0) {
  316                       positiveBase = positiveBase + d;
  317                   }
  318                   else {
  319                       negativeBase = negativeBase + d;
  320                   }
  321               }
  322           }
  323   
  324           double translatedBase;
  325           double translatedValue;
  326           RectangleEdge location = plot.getRangeAxisEdge();
  327           if (value >= 0.0) {
  328               translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea,
  329                       location);
  330               translatedValue = rangeAxis.valueToJava2D(positiveBase + value,
  331                       dataArea, location);
  332           }
  333           else {
  334               translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea,
  335                       location);
  336               translatedValue = rangeAxis.valueToJava2D(negativeBase + value,
  337                       dataArea, location);
  338           }
  339           double barL0 = Math.min(translatedBase, translatedValue);
  340           double barLength = Math.max(Math.abs(translatedValue - translatedBase),
  341                   getMinimumBarLength());
  342   
  343           Rectangle2D bar = null;
  344           if (orientation == PlotOrientation.HORIZONTAL) {
  345               bar = new Rectangle2D.Double(barL0, barW0, barLength,
  346                       state.getBarWidth());
  347           }
  348           else {
  349               bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
  350                       barLength);
  351           }
  352           if (pass == 0) {
  353               Paint itemPaint = getItemPaint(row, column);
  354               GradientPaintTransformer t = getGradientPaintTransformer();
  355               if (t != null && itemPaint instanceof GradientPaint) {
  356                   itemPaint = t.transform((GradientPaint) itemPaint, bar);
  357               }
  358               g2.setPaint(itemPaint);
  359               g2.fill(bar);
  360               if (isDrawBarOutline()
  361                       && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
  362                   g2.setStroke(getItemOutlineStroke(row, column));
  363                   g2.setPaint(getItemOutlinePaint(row, column));
  364                   g2.draw(bar);
  365               }
  366   
  367               // add an item entity, if this information is being collected
  368               EntityCollection entities = state.getEntityCollection();
  369               if (entities != null) {
  370                   addItemEntity(entities, dataset, row, column, bar);
  371               }
  372           }
  373           else if (pass == 1) {
  374               CategoryItemLabelGenerator generator = getItemLabelGenerator(row,
  375                       column);
  376               if (generator != null && isItemLabelVisible(row, column)) {
  377                   drawItemLabel(g2, dataset, row, column, plot, generator, bar,
  378                           (value < 0.0));
  379               }
  380           }
  381       }
  382   
  383       /**
  384        * Tests this renderer for equality with an arbitrary object.
  385        *
  386        * @param obj  the object (<code>null</code> permitted).
  387        *
  388        * @return A boolean.
  389        */
  390       public boolean equals(Object obj) {
  391           if (obj == this) {
  392               return true;
  393           }
  394           if (!(obj instanceof StackedBarRenderer)) {
  395               return false;
  396           }
  397           StackedBarRenderer that = (StackedBarRenderer) obj;
  398           if (this.renderAsPercentages != that.renderAsPercentages) {
  399               return false;
  400           }
  401           return super.equals(obj);
  402       }
  403   
  404   }

Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » category » [javadoc | source]