Richard Stephens avatar Richard Stephens committed d506909

initial checkin of fecru chart types

Comments (0)

Files changed (3)

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.atlassian.fecru.charts</groupId>
+    <artifactId>fecru-chart-types</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>jfree</groupId>
+            <artifactId>jfreechart</artifactId>
+            <version>1.0.9</version>
+        </dependency>
+        <dependency> <!-- this can be removed when jfreechart has a real pom -->
+            <groupId>jfree</groupId>
+            <artifactId>jcommon</artifactId>
+            <version>1.0.12</version>
+        </dependency>
+    </dependencies>
+</project>

src/main/java/com/atlassian/fecru/charts/ExtendedStackedBarRenderer.java

+package com.atlassian.fecru.charts;
+
+import org.jfree.chart.axis.CategoryAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.CategoryItemEntity;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.labels.CategoryToolTipGenerator;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.renderer.category.CategoryItemRendererState;
+import org.jfree.chart.renderer.category.StackedBarRenderer;
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.text.TextUtilities;
+import org.jfree.ui.RectangleEdge;
+import org.jfree.ui.TextAnchor;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.geom.Rectangle2D;
+import java.text.NumberFormat;
+
+/**
+ * An extension of the {@link StackedBarRenderer} that can draw positive and
+ * negative totals at the top and bottom of the stacked bars.
+ *
+ * NOTE: This class has been copied from the demo source files bundled
+ * with the jfreechart source.
+ */
+public class ExtendedStackedBarRenderer extends StackedBarRenderer {
+
+    /** Show positive label? */
+    private boolean showPositiveTotal = true;
+
+    /** Show negative label? */
+    private boolean showNegativeTotal = true;
+
+    /** Font for labels. */
+    private Font totalLabelFont = new Font("SansSerif", Font.PLAIN, 10);
+
+    /** Formatter for total. */
+    private NumberFormat totalFormatter;
+
+    /**
+     * Creates a new renderer.
+     */
+    public ExtendedStackedBarRenderer() {
+        super();
+        this.totalFormatter = NumberFormat.getInstance();
+    }
+
+    /**
+     * Returns the total formatter.
+     *
+     * @return the total formatter (never <code>null</code>).
+     */
+    public NumberFormat getTotalFormatter() {
+        return this.totalFormatter;
+    }
+
+    /**
+     * Sets the total formatter.
+     *
+     * @param format  the formatter (<code>null</code> not permitted).
+     */
+    public void setTotalFormatter(NumberFormat format) {
+        if (format == null) {
+            throw new IllegalArgumentException("Null format not permitted.");
+        }
+        this.totalFormatter = format;
+    }
+
+    /**
+     * Draws a stacked bar for a specific item.
+     *
+     * @param g2  the graphics device.
+     * @param state  the renderer state.
+     * @param dataArea  the plot area.
+     * @param plot  the plot.
+     * @param domainAxis  the domain (category) axis.
+     * @param rangeAxis  the range (value) axis.
+     * @param dataset  the data.
+     * @param row  the row index (zero-based).
+     * @param column  the column index (zero-based).
+     * @param pass  the pass index.
+     */
+    public void drawItem(Graphics2D g2,
+                         CategoryItemRendererState state,
+                         Rectangle2D dataArea,
+                         CategoryPlot plot,
+                         CategoryAxis domainAxis,
+                         ValueAxis rangeAxis,
+                         CategoryDataset dataset,
+                         int row,
+                         int column,
+                         int pass) {
+
+        // nothing is drawn for null values...
+        Number dataValue = dataset.getValue(row, column);
+        if (dataValue == null) {
+            return;
+        }
+
+        double value = dataValue.doubleValue();
+
+        PlotOrientation orientation = plot.getOrientation();
+        double barW0 = domainAxis.getCategoryMiddle(
+                column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
+        ) - state.getBarWidth() / 2.0;
+
+        double positiveBase = 0.0;
+        double negativeBase = 0.0;
+
+        for (int i = 0; i < row; i++) {
+            Number v = dataset.getValue(i, column);
+            if (v != null) {
+                double d = v.doubleValue();
+                if (d > 0) {
+                    positiveBase = positiveBase + d;
+                } else {
+                    negativeBase = negativeBase + d;
+                }
+            }
+        }
+
+        double translatedBase;
+        double translatedValue;
+        RectangleEdge location = plot.getRangeAxisEdge();
+        if (value > 0.0) {
+            translatedBase = rangeAxis.valueToJava2D(
+                    positiveBase, dataArea, location
+            );
+            translatedValue = rangeAxis.valueToJava2D(
+                    positiveBase + value, dataArea, location
+            );
+        } else {
+            translatedBase = rangeAxis.valueToJava2D(
+                    negativeBase, dataArea, location
+            );
+            translatedValue = rangeAxis.valueToJava2D(
+                    negativeBase + value, dataArea, location
+            );
+        }
+        double barL0 = Math.min(translatedBase, translatedValue);
+        double barLength = Math.max(
+                Math.abs(translatedValue - translatedBase), getMinimumBarLength()
+        );
+
+        Rectangle2D bar = null;
+        if (orientation == PlotOrientation.HORIZONTAL) {
+            bar = new Rectangle2D.Double(
+                    barL0, barW0, barLength, state.getBarWidth()
+            );
+        } else {
+            bar = new Rectangle2D.Double(
+                    barW0, barL0, state.getBarWidth(), barLength
+            );
+        }
+        Paint seriesPaint = getItemPaint(row, column);
+        g2.setPaint(seriesPaint);
+        g2.fill(bar);
+        if (isDrawBarOutline() && state.getBarWidth()
+                > BAR_OUTLINE_WIDTH_THRESHOLD) {
+            g2.setStroke(getItemStroke(row, column));
+            g2.setPaint(getItemOutlinePaint(row, column));
+            g2.draw(bar);
+        }
+
+        if (value > 0.0) {
+            if (this.showPositiveTotal) {
+                if (isLastPositiveItem(dataset, row, column)) {
+                    g2.setPaint(Color.black);
+                    g2.setFont(this.totalLabelFont);
+                    double total = calculateSumOfPositiveValuesForCategory(
+                            dataset, column
+                    );
+                    TextUtilities.drawRotatedString(
+                            this.totalFormatter.format(total), g2,
+                            (float) bar.getCenterX(),
+                            (float) (bar.getMinY() - 4.0),
+                            TextAnchor.BOTTOM_CENTER,
+                            0.0,
+                            TextAnchor.BOTTOM_CENTER
+                    );
+                }
+            }
+        } else {
+            if (this.showNegativeTotal) {
+                if (isLastNegativeItem(dataset, row, column)) {
+                    g2.setPaint(Color.black);
+                    g2.setFont(this.totalLabelFont);
+                    double total = calculateSumOfNegativeValuesForCategory(
+                            dataset, column
+                    );
+                    TextUtilities.drawRotatedString(
+                            String.valueOf(total), g2,
+                            (float) bar.getCenterX(),
+                            (float) (bar.getMaxY() + 4.0),
+                            TextAnchor.TOP_CENTER,
+                            0.0,
+                            TextAnchor.TOP_CENTER
+                    );
+                }
+            }
+        }
+
+        // collect entity and tool tip information...
+        if (state.getInfo() != null) {
+            EntityCollection entities = state.getEntityCollection();
+            if (entities != null) {
+                String tip = null;
+                CategoryToolTipGenerator tipster = getToolTipGenerator(
+                        row, column
+                );
+                if (tipster != null) {
+                    tip = tipster.generateToolTip(dataset, row, column);
+                }
+                String url = null;
+                if (getItemURLGenerator(row, column) != null) {
+                    url = getItemURLGenerator(row, column).generateURL(
+                            dataset, row, column
+                    );
+                }
+                CategoryItemEntity entity = new CategoryItemEntity(
+                        bar, tip, url, dataset, row, dataset.getColumnKey(column),
+                        column
+                );
+                entities.add(entity);
+            }
+        }
+
+    }
+
+    /**
+     * Returns true if the specified item is the last positive value for that
+     * category.
+     *
+     * @param dataset  the dataset.
+     * @param row  the row (series).
+     * @param column  the column (category).
+     *
+     * @return a boolean.
+     */
+    private boolean isLastPositiveItem(CategoryDataset dataset,
+                                       int row,
+                                       int column) {
+        boolean result = true;
+        Number dataValue = dataset.getValue(row, column);
+        if (dataValue == null) {
+            return false;  // value is null
+        }
+        for (int r = row + 1; r < dataset.getRowCount(); r++) {
+            dataValue = dataset.getValue(r, column);
+            if (dataValue != null) {
+                result = result && (dataValue.doubleValue() <= 0.0);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns true if the specified item is the last negative value for that
+     * category.
+     *
+     * @param dataset  the dataset.
+     * @param row  the row (series).
+     * @param column  the column (category).
+     *
+     * @return a boolean.
+     */
+    private boolean isLastNegativeItem(CategoryDataset dataset,
+                                       int row,
+                                       int column) {
+        boolean result = true;
+        Number dataValue = dataset.getValue(row, column);
+        if (dataValue == null) {
+            return false;  // value is null
+        }
+        for (int r = row + 1; r < dataset.getRowCount(); r++) {
+            dataValue = dataset.getValue(r, column);
+            if (dataValue != null) {
+                result = result && (dataValue.doubleValue() >= 0.0);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Calculates the sum of the positive values within a category.
+     *
+     * @param dataset  the dataset.
+     * @param column  the column (category).
+     *
+     * @return the sum of the positive values.
+     */
+    private double calculateSumOfPositiveValuesForCategory(
+            CategoryDataset dataset, int column) {
+        double result = 0.0;
+        for (int r = 0; r < dataset.getRowCount(); r++) {
+            Number dataValue = dataset.getValue(r, column);
+            if (dataValue != null) {
+                double v = dataValue.doubleValue();
+                if (v > 0.0) {
+                    result = result + v;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Calculates the sum of the negative values within a category.
+     *
+     * @param dataset  the dataset.
+     * @param column  the column (category).
+     *
+     * @return the sum of the negative values.
+     */
+    private double calculateSumOfNegativeValuesForCategory(
+            CategoryDataset dataset, int column) {
+        double result = 0.0;
+        for (int r = 0; r < dataset.getRowCount(); r++) {
+            Number dataValue = dataset.getValue(r, column);
+            if (dataValue != null) {
+                double v = dataValue.doubleValue();
+                if (v < 0.0) {
+                    result = result + v;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Use this to stop numbers appearing on the top of the bars
+     *
+     * @param value false to stop, true to show
+     */
+    public void setDomainTickMarksVisible(boolean value) {
+        this.showNegativeTotal = value;
+        this.showPositiveTotal = value;
+    }
+
+}
+

src/main/java/com/atlassian/fecru/charts/StackedXYAreaRenderer2TopLine.java

+package com.atlassian.fecru.charts;
+
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.labels.XYSeriesLabelGenerator;
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2;
+import org.jfree.chart.renderer.xy.XYItemRendererState;
+import org.jfree.data.xy.TableXYDataset;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.ui.RectangleEdge;
+
+import java.awt.*;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Created by Anna
+ * Mar 3, 2008, 4:35:14 PM
+ */
+public class StackedXYAreaRenderer2TopLine extends StackedXYAreaRenderer2 {
+
+    public static final Shape DEFAULT_SHAPE = new Rectangle(10, 10);
+
+    /**
+     * The shape used to represent an area in each legend item (this should
+     * never be <code>null</code>).
+     */
+    private transient Shape legendArea;
+
+
+    public int getPassCount() {
+        return 2;
+    }
+
+    public void drawItem(Graphics2D g2,
+                         XYItemRendererState state,
+                         Rectangle2D dataArea,
+                         PlotRenderingInfo info,
+                         XYPlot plot,
+                         ValueAxis domainAxis,
+                         ValueAxis rangeAxis,
+                         XYDataset dataset,
+                         int series,
+                         int item,
+                         CrosshairState crosshairState,
+                         int pass) {
+
+        // setup for collecting optional entity info...
+        Shape entityArea = null;
+        EntityCollection entities = null;
+        if (info != null) {
+            entities = info.getOwner().getEntityCollection();
+        }
+
+        TableXYDataset tdataset = (TableXYDataset) dataset;
+
+        // get the data point...
+        double x1 = dataset.getXValue(series, item);
+        double y1 = dataset.getYValue(series, item);
+        if (Double.isNaN(y1)) {
+            y1 = 0.0;
+        }
+        double[] stack1 = getStackValues(tdataset, series, item);
+
+        // get the previous point and the next point so we can calculate a
+        // "hot spot" for the area (used by the chart entity)...
+        double x0 = dataset.getXValue(series, Math.max(item - 1, 0));
+        double y0 = dataset.getYValue(series, Math.max(item - 1, 0));
+        if (Double.isNaN(y0)) {
+            y0 = 0.0;
+        }
+        double[] stack0 = getStackValues(tdataset, series, Math.max(item - 1,
+                0));
+
+        int itemCount = dataset.getItemCount(series);
+        double x2 = dataset.getXValue(series, Math.min(item + 1,
+                itemCount - 1));
+        double y2 = dataset.getYValue(series, Math.min(item + 1,
+                itemCount - 1));
+        if (Double.isNaN(y2)) {
+            y2 = 0.0;
+        }
+        double[] stack2 = getStackValues(tdataset, series, Math.min(item + 1,
+                itemCount - 1));
+
+        double xleft = (x0 + x1) / 2.0;
+        double xright = (x1 + x2) / 2.0;
+        double[] stackLeft = averageStackValues(stack0, stack1);
+        double[] stackRight = averageStackValues(stack1, stack2);
+        double[] adjStackLeft = adjustedStackValues(stack0, stack1);
+        double[] adjStackRight = adjustedStackValues(stack1, stack2);
+
+        RectangleEdge edge0 = plot.getDomainAxisEdge();
+
+        float transX1 = (float) domainAxis.valueToJava2D(x1, dataArea, edge0);
+        float transXLeft = (float) domainAxis.valueToJava2D(xleft, dataArea,
+                edge0);
+        float transXRight = (float) domainAxis.valueToJava2D(xright, dataArea,
+                edge0);
+
+        if (this.getRoundXCoordinates()) {
+            transX1 = Math.round(transX1);
+            transXLeft = Math.round(transXLeft);
+            transXRight = Math.round(transXRight);
+        }
+        float transY1;
+
+        RectangleEdge edge1 = plot.getRangeAxisEdge();
+
+        GeneralPath left = new GeneralPath();
+        GeneralPath right = new GeneralPath();
+        int lX1 = 0;
+        int lY1 = 0;
+        int lX2 = 0;
+        int lY2 = 0;
+
+        int rX1 = 0;
+        int rY1 = 0;
+        int rX2 = 0;
+        int rY2 = 0;
+
+        if (y1 >= 0.0) {  // handle positive value
+            transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[1], dataArea,
+                    edge1);
+            float transStack1 = (float) rangeAxis.valueToJava2D(stack1[1],
+                    dataArea, edge1);
+            float transStackLeft = (float) rangeAxis.valueToJava2D(
+                    adjStackLeft[1], dataArea, edge1);
+
+            // LEFT POLYGON
+            if (y0 >= 0.0) {
+                double yleft = (y0 + y1) / 2.0 + stackLeft[1];
+                float transYLeft
+                        = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1);
+                left.moveTo(transX1, transY1);
+                left.lineTo(transX1, transStack1);
+                left.lineTo(transXLeft, transStackLeft);
+                left.lineTo(transXLeft, transYLeft);
+                left.closePath();
+                lY2 = (int) transYLeft;
+            } else {
+                left.moveTo(transX1, transStack1);
+                left.lineTo(transX1, transY1);
+                left.lineTo(transXLeft, transStackLeft);
+                left.closePath();
+                lY2 = (int) transY1;
+            }
+            lX1 = (int) transX1;
+            lY1 = (int) transY1;
+            lX2 = (int) transXLeft;
+
+            float transStackRight = (float) rangeAxis.valueToJava2D(
+                    adjStackRight[1], dataArea, edge1);
+            // RIGHT POLYGON
+            if (y2 >= 0.0) {
+                double yright = (y1 + y2) / 2.0 + stackRight[1];
+                float transYRight
+                        = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1);
+                right.moveTo(transX1, transStack1);
+                right.lineTo(transX1, transY1);
+                right.lineTo(transXRight, transYRight);
+                right.lineTo(transXRight, transStackRight);
+                right.closePath();
+                rY2 = (int) transYRight;
+            } else {
+                right.moveTo(transX1, transStack1);
+                right.lineTo(transX1, transY1);
+                right.lineTo(transXRight, transStackRight);
+                right.closePath();
+                rY2 = (int) transY1;
+            }
+            rX1 = (int) transX1;
+            rY1 = (int) transY1;
+            rX2 = (int) transXRight;
+
+        } else {  // handle negative value
+            transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[0], dataArea,
+                    edge1);
+            float transStack1 = (float) rangeAxis.valueToJava2D(stack1[0],
+                    dataArea, edge1);
+            float transStackLeft = (float) rangeAxis.valueToJava2D(
+                    adjStackLeft[0], dataArea, edge1);
+
+            // LEFT POLYGON
+            if (y0 >= 0.0) {
+                left.moveTo(transX1, transStack1);
+                left.lineTo(transX1, transY1);
+                left.lineTo(transXLeft, transStackLeft);
+                left.clone();
+            } else {
+                double yleft = (y0 + y1) / 2.0 + stackLeft[0];
+                float transYLeft = (float) rangeAxis.valueToJava2D(yleft,
+                        dataArea, edge1);
+                left.moveTo(transX1, transY1);
+                left.lineTo(transX1, transStack1);
+                left.lineTo(transXLeft, transStackLeft);
+                left.lineTo(transXLeft, transYLeft);
+                left.closePath();
+            }
+            float transStackRight = (float) rangeAxis.valueToJava2D(
+                    adjStackRight[0], dataArea, edge1);
+
+            // RIGHT POLYGON
+            if (y2 >= 0.0) {
+                right.moveTo(transX1, transStack1);
+                right.lineTo(transX1, transY1);
+                right.lineTo(transXRight, transStackRight);
+                right.closePath();
+            } else {
+                double yright = (y1 + y2) / 2.0 + stackRight[0];
+                float transYRight = (float) rangeAxis.valueToJava2D(yright,
+                        dataArea, edge1);
+                right.moveTo(transX1, transStack1);
+                right.lineTo(transX1, transY1);
+                right.lineTo(transXRight, transYRight);
+                right.lineTo(transXRight, transStackRight);
+                right.closePath();
+            }
+        }
+
+        //  Get series Paint and Stroke
+        Paint itemPaint = getItemPaint(series, item);
+        if (pass == 0) {
+            g2.setPaint(itemPaint);
+            g2.fill(left);
+            g2.fill(right);
+            Paint itemPaintOutline = getItemOutlinePaint(series, item);
+            Stroke itemStrokeOutline = getItemOutlineStroke(series, item);
+
+            g2.setPaint(itemPaint);
+            g2.setStroke(itemStrokeOutline);
+
+//            g2.draw(left);
+//            g2.draw(right);
+        }
+        Paint itemPaintOutline = getItemOutlinePaint(series, item);
+        Stroke itemStrokeOutline = getItemOutlineStroke(series, item);
+
+        if (itemPaintOutline != null && itemStrokeOutline != null) {
+            g2.setPaint(itemPaintOutline);
+            g2.setStroke(itemStrokeOutline);
+            g2.drawLine(lX1, lY1, lX2, lY2);
+            g2.drawLine(rX1, rY1, rX2, rY2);
+
+        }
+
+        // add an entity for the item...
+        if (entities != null) {
+            GeneralPath gp = new GeneralPath(left);
+            gp.append(right, false);
+            entityArea = gp;
+            addEntity(entities, entityArea, dataset, series, item, transX1, transY1);
+        }
+
+    }
+
+    /**
+     * Calculates the stacked values (one positive and one negative) of all
+     * series up to, but not including, <code>series</code> for the specified
+     * item. It returns [0.0, 0.0] if <code>series</code> is the first series.
+     *
+     * @param dataset the dataset (<code>null</code> not permitted).
+     * @param series the series index.
+     * @param index the item index.
+     * @return An array containing the cumulative negative and positive values
+     *         for all series values up to but excluding <code>series</code>
+     *         for <code>index</code>.
+     */
+    private double[] getStackValues(TableXYDataset dataset,
+                                    int series, int index) {
+        double[] result = new double[2];
+        for (int i = 0; i < series; i++) {
+            double v = dataset.getYValue(i, index);
+            if (!Double.isNaN(v)) {
+                if (v >= 0.0) {
+                    result[1] += v;
+                } else {
+                    result[0] += v;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Calculates adjusted stack values from the supplied values.  The value is
+     * the mean of the supplied values, unless either of the supplied values
+     * is zero, in which case the adjusted value is zero also.
+     *
+     * @param stack1 the first stack pair.
+     * @param stack2 the second stack pair.
+     * @return A pair of average stack values.
+     */
+    private double[] adjustedStackValues(double[] stack1, double[] stack2) {
+        double[] result = new double[2];
+        if (stack1[0] == 0.0 || stack2[0] == 0.0) {
+            result[0] = 0.0;
+        } else {
+            result[0] = (stack1[0] + stack2[0]) / 2.0;
+        }
+        if (stack1[1] == 0.0 || stack2[1] == 0.0) {
+            result[1] = 0.0;
+        } else {
+            result[1] = (stack1[1] + stack2[1]) / 2.0;
+        }
+        return result;
+    }
+
+    /**
+     * Returns a pair of "stack" values calculated as the mean of the two
+     * specified stack value pairs.
+     *
+     * @param stack1 the first stack pair.
+     * @param stack2 the second stack pair.
+     * @return A pair of average stack values.
+     */
+    private double[] averageStackValues(double[] stack1, double[] stack2) {
+        double[] result = new double[2];
+        result[0] = (stack1[0] + stack2[0]) / 2.0;
+        result[1] = (stack1[1] + stack2[1]) / 2.0;
+        return result;
+    }
+
+    /**
+     * Returns a default legend item for the specified series.  Subclasses
+     * should override this method to generate customised items.
+     *
+     * @param datasetIndex the dataset index (zero-based).
+     * @param series the series index (zero-based).
+     * @return A legend item for the series.
+     */
+    public LegendItem getLegendItem(int datasetIndex, int series) {
+        LegendItem result = null;
+        XYPlot xyplot = getPlot();
+        if (xyplot != null) {
+            XYDataset dataset = xyplot.getDataset(datasetIndex);
+            if (dataset != null) {
+                XYSeriesLabelGenerator lg = getLegendItemLabelGenerator();
+                String label = lg.generateLabel(dataset, series);
+                String description = label;
+                String toolTipText = null;
+                if (getLegendItemToolTipGenerator() != null) {
+                    toolTipText = getLegendItemToolTipGenerator().generateLabel(
+                            dataset, series);
+                }
+                String urlText = null;
+                if (getLegendItemURLGenerator() != null) {
+                    urlText = getLegendItemURLGenerator().generateLabel(
+                            dataset, series);
+                }
+                Paint paint = lookupSeriesPaint(series);
+                Paint outlinePaint = lookupSeriesOutlinePaint(series);
+                Stroke outlineStroke = lookupSeriesOutlineStroke(series);
+
+                result = new LegendItem(label, description, toolTipText,
+                        urlText, this.legendArea, paint, outlineStroke, outlinePaint);
+                result.setDataset(dataset);
+                result.setDatasetIndex(datasetIndex);
+                result.setSeriesKey(dataset.getSeriesKey(series));
+                result.setSeriesIndex(series);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns the shape used to represent an area in the legend.
+     *
+     * @return The legend area (never <code>null</code>).
+     * @see #setLegendArea(Shape)
+     */
+    public Shape getLegendArea() {
+        return this.legendArea;
+    }
+
+    /**
+     * Sets the shape used as an area in each legend item and sends a
+     * {@link org.jfree.chart.event.RendererChangeEvent} to all registered listeners.
+     *
+     * @param area the area (<code>null</code> not permitted).
+     * @see #getLegendArea()
+     */
+    public void setLegendArea(Shape area) {
+        if (area == null) {
+            throw new IllegalArgumentException("Null 'area' argument.");
+        }
+        this.legendArea = area;
+        fireChangeEvent();
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.