net.sf.jasperreports.engine.fill.JRFillChart.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.engine.fill.JRFillChart.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.jasperreports.engine.fill;

import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TimeZone;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.MeterInterval;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.Range;
import org.jfree.data.general.Dataset;

import net.sf.jasperreports.charts.ChartContext;
import net.sf.jasperreports.charts.ChartTheme;
import net.sf.jasperreports.charts.JRAreaPlot;
import net.sf.jasperreports.charts.JRBar3DPlot;
import net.sf.jasperreports.charts.JRBarPlot;
import net.sf.jasperreports.charts.JRBubblePlot;
import net.sf.jasperreports.charts.JRCandlestickPlot;
import net.sf.jasperreports.charts.JRCategoryDataset;
import net.sf.jasperreports.charts.JRChartAxis;
import net.sf.jasperreports.charts.JRDataRange;
import net.sf.jasperreports.charts.JRGanttDataset;
import net.sf.jasperreports.charts.JRHighLowDataset;
import net.sf.jasperreports.charts.JRHighLowPlot;
import net.sf.jasperreports.charts.JRLinePlot;
import net.sf.jasperreports.charts.JRMeterPlot;
import net.sf.jasperreports.charts.JRMultiAxisPlot;
import net.sf.jasperreports.charts.JRPie3DPlot;
import net.sf.jasperreports.charts.JRPieDataset;
import net.sf.jasperreports.charts.JRPiePlot;
import net.sf.jasperreports.charts.JRScatterPlot;
import net.sf.jasperreports.charts.JRThermometerPlot;
import net.sf.jasperreports.charts.JRTimePeriodDataset;
import net.sf.jasperreports.charts.JRTimeSeriesDataset;
import net.sf.jasperreports.charts.JRTimeSeriesPlot;
import net.sf.jasperreports.charts.JRValueDataset;
import net.sf.jasperreports.charts.JRXyDataset;
import net.sf.jasperreports.charts.JRXyzDataset;
import net.sf.jasperreports.charts.fill.JRFillCategoryDataset;
import net.sf.jasperreports.charts.fill.JRFillChartAxis;
import net.sf.jasperreports.charts.fill.JRFillGanttDataset;
import net.sf.jasperreports.charts.fill.JRFillHighLowDataset;
import net.sf.jasperreports.charts.fill.JRFillMultiAxisPlot;
import net.sf.jasperreports.charts.fill.JRFillPieDataset;
import net.sf.jasperreports.charts.fill.JRFillTimePeriodDataset;
import net.sf.jasperreports.charts.fill.JRFillTimeSeriesDataset;
import net.sf.jasperreports.charts.fill.JRFillXyDataset;
import net.sf.jasperreports.charts.fill.JRFillXyzDataset;
import net.sf.jasperreports.charts.type.AxisPositionEnum;
import net.sf.jasperreports.charts.type.EdgeEnum;
import net.sf.jasperreports.charts.util.CategoryChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.ChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.ChartUtil;
import net.sf.jasperreports.charts.util.HighLowChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.JRMeterInterval;
import net.sf.jasperreports.charts.util.MultiAxisChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.PieChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.TimePeriodChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.TimeSeriesChartHyperlinkProvider;
import net.sf.jasperreports.charts.util.XYChartHyperlinkProvider;
import net.sf.jasperreports.engine.JRAbstractChartCustomizer;
import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRChartCustomizer;
import net.sf.jasperreports.engine.JRChartDataset;
import net.sf.jasperreports.engine.JRChartPlot;
import net.sf.jasperreports.engine.JRChartPlot.JRSeriesColor;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionCollector;
import net.sf.jasperreports.engine.JRFont;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRHyperlinkParameter;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintHyperlinkParameters;
import net.sf.jasperreports.engine.JRPrintImage;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRVisitor;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.NamedChartCustomizer;
import net.sf.jasperreports.engine.base.JRBaseChart;
import net.sf.jasperreports.engine.type.EvaluationTimeEnum;
import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.util.JRClassLoader;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.StyleUtil;
import net.sf.jasperreports.export.HtmlReportConfiguration;
import net.sf.jasperreports.renderers.Renderable;

/**
 * @author Teodor Danciu (teodord@users.sourceforge.net)
 * @author Some enhancements by Barry Klawans (bklawans@users.sourceforge.net)
 */
public class JRFillChart extends JRFillElement implements JRChart {
    public static final String EXCEPTION_MESSAGE_KEY_CUSTOMIZER_INSTANCE_ERROR = "charts.customizer.instance.error";
    public static final String EXCEPTION_MESSAGE_KEY_MULTIAXIS_PLOT_TYPES_MIX_NOT_ALLOWED = "charts.multiaxis.plot.types.mix.not.allowed";
    public static final String EXCEPTION_MESSAGE_KEY_MULTIAXIS_PLOT_NOT_SUPPORTED = "charts.multiaxis.plot.not.supported";

    /**
     *
     */
    protected byte chartType;

    /**
     *
     */
    protected JRFont titleFont;
    protected JRFont subtitleFont;
    protected JRFont legendFont;

    protected final JRLineBox initLineBox;
    protected JRLineBox lineBox;

    /**
     *
     */
    protected JRGroup evaluationGroup;

    protected JRFillChartDataset dataset;
    protected JRChartPlot plot;

    protected Renderable renderer;
    private String anchorName;
    private String hyperlinkReference;
    private Boolean hyperlinkWhen;
    private String hyperlinkAnchor;
    private Integer hyperlinkPage;
    private String hyperlinkTooltip;
    private JRPrintHyperlinkParameters hyperlinkParameters;

    protected List<JRChartCustomizer> chartCustomizers;

    protected String renderType;
    protected ChartTheme theme;

    protected JFreeChart jfreeChart;
    protected ChartHyperlinkProvider chartHyperlinkProvider;

    /**
     *
     */
    protected JRFillChart(JRBaseFiller filler, JRChart chart, JRFillObjectFactory factory) {
        super(filler, chart, factory);

        /*   */
        chartType = chart.getChartType();

        switch (chartType) {
        case CHART_TYPE_AREA:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getAreaPlot((JRAreaPlot) chart.getPlot());
            break;
        case CHART_TYPE_BAR:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getBarPlot((JRBarPlot) chart.getPlot());
            break;
        case CHART_TYPE_BAR3D:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getBar3DPlot((JRBar3DPlot) chart.getPlot());
            break;
        case CHART_TYPE_BUBBLE:
            dataset = (JRFillChartDataset) factory.getXyzDataset((JRXyzDataset) chart.getDataset());
            plot = factory.getBubblePlot((JRBubblePlot) chart.getPlot());
            break;
        case CHART_TYPE_CANDLESTICK:
            dataset = (JRFillChartDataset) factory.getHighLowDataset((JRHighLowDataset) chart.getDataset());
            plot = factory.getCandlestickPlot((JRCandlestickPlot) chart.getPlot());
            break;
        case CHART_TYPE_HIGHLOW:
            dataset = (JRFillChartDataset) factory.getHighLowDataset((JRHighLowDataset) chart.getDataset());
            plot = factory.getHighLowPlot((JRHighLowPlot) chart.getPlot());
            break;
        case CHART_TYPE_LINE:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getLinePlot((JRLinePlot) chart.getPlot());
            break;
        case CHART_TYPE_METER:
            dataset = (JRFillChartDataset) factory.getValueDataset((JRValueDataset) chart.getDataset());
            plot = factory.getMeterPlot((JRMeterPlot) chart.getPlot());
            break;
        case CHART_TYPE_MULTI_AXIS:
            plot = factory.getMultiAxisPlot((JRMultiAxisPlot) chart.getPlot());
            dataset = ((JRFillMultiAxisPlot) plot).getMainDataset();
            break;
        case CHART_TYPE_PIE:
            dataset = (JRFillChartDataset) factory.getPieDataset((JRPieDataset) chart.getDataset());
            plot = factory.getPiePlot((JRPiePlot) chart.getPlot());
            break;
        case CHART_TYPE_PIE3D:
            dataset = (JRFillChartDataset) factory.getPieDataset((JRPieDataset) chart.getDataset());
            plot = factory.getPie3DPlot((JRPie3DPlot) chart.getPlot());
            break;
        case CHART_TYPE_SCATTER:
            dataset = (JRFillChartDataset) factory.getXyDataset((JRXyDataset) chart.getDataset());
            plot = factory.getScatterPlot((JRScatterPlot) chart.getPlot());
            break;
        case CHART_TYPE_STACKEDBAR:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getBarPlot((JRBarPlot) chart.getPlot());
            break;
        case CHART_TYPE_STACKEDBAR3D:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getBar3DPlot((JRBar3DPlot) chart.getPlot());
            break;
        case CHART_TYPE_THERMOMETER:
            dataset = (JRFillChartDataset) factory.getValueDataset((JRValueDataset) chart.getDataset());
            plot = factory.getThermometerPlot((JRThermometerPlot) chart.getPlot());
            break;
        case CHART_TYPE_TIMESERIES:
            dataset = (JRFillChartDataset) factory.getTimeSeriesDataset((JRTimeSeriesDataset) chart.getDataset());
            plot = factory.getTimeSeriesPlot((JRTimeSeriesPlot) chart.getPlot());
            break;
        case CHART_TYPE_XYAREA:
            dataset = (JRFillChartDataset) factory.getXyDataset((JRXyDataset) chart.getDataset());
            plot = factory.getAreaPlot((JRAreaPlot) chart.getPlot());
            break;
        case CHART_TYPE_XYBAR:
            switch (chart.getDataset().getDatasetType()) {
            case JRChartDataset.TIMESERIES_DATASET:
                dataset = (JRFillChartDataset) factory
                        .getTimeSeriesDataset((JRTimeSeriesDataset) chart.getDataset());
                break;
            case JRChartDataset.TIMEPERIOD_DATASET:
                dataset = (JRFillChartDataset) factory
                        .getTimePeriodDataset((JRTimePeriodDataset) chart.getDataset());
                break;
            case JRChartDataset.XY_DATASET:
                dataset = (JRFillChartDataset) factory.getXyDataset((JRXyDataset) chart.getDataset());
                break;
            }

            plot = factory.getBarPlot((JRBarPlot) chart.getPlot());
            break;
        case CHART_TYPE_XYLINE:
            dataset = (JRFillChartDataset) factory.getXyDataset((JRXyDataset) chart.getDataset());
            plot = factory.getLinePlot((JRLinePlot) chart.getPlot());
            break;
        case CHART_TYPE_STACKEDAREA:
            dataset = (JRFillChartDataset) factory.getCategoryDataset((JRCategoryDataset) chart.getDataset());
            plot = factory.getAreaPlot((JRAreaPlot) chart.getPlot());
            break;
        case CHART_TYPE_GANTT:
            dataset = (JRFillChartDataset) factory.getGanttDataset((JRGanttDataset) chart.getDataset());
            plot = factory.getBarPlot((JRBarPlot) chart.getPlot());
            break;
        default:
            throw new JRRuntimeException(JRBaseChart.EXCEPTION_MESSAGE_KEY_CHART_TYPE_NOT_SUPPORTED,
                    new Object[] { getChartType() });
        }

        titleFont = factory.getFont(chart, chart.getTitleFont());
        subtitleFont = factory.getFont(chart, chart.getSubtitleFont());
        //FIXME this is inconsistent with the lines above
        legendFont = factory.getFont(this, chart.getLegendFont());

        initLineBox = chart.getLineBox().clone(this);

        evaluationGroup = factory.getGroup(chart.getEvaluationGroup());

        chartCustomizers = new ArrayList<JRChartCustomizer>();
        JRChartCustomizer chartCustomizer = createAndInitCustomizer(chart.getCustomizerClass(), null);
        if (chartCustomizer != null) {
            chartCustomizers.add(chartCustomizer);
        }

        List<JRPropertiesUtil.PropertySuffix> properties = JRPropertiesUtil.getProperties(chart.getPropertiesMap(),
                NamedChartCustomizer.CUSTOMIZER_CLASS_PROPERTY_PREFIX);
        for (JRPropertiesUtil.PropertySuffix prop : properties) {
            chartCustomizer = createAndInitCustomizer(prop.getValue(), prop.getSuffix());
            if (chartCustomizer != null) {
                chartCustomizers.add(chartCustomizer);
            }
        }

        renderType = chart.getRenderType();
        if (renderType == null) {
            renderType = filler.getPropertiesUtil().getProperty(getParentProperties(),
                    JRChart.PROPERTY_CHART_RENDER_TYPE);
        }
    }

    /**
     *
     */
    protected JRChartCustomizer createAndInitCustomizer(String customizerClassName, String customizerName) {
        JRChartCustomizer customizer = null;

        if (customizerClassName != null && customizerClassName.length() > 0) {
            try {
                Class<?> customizerClass = JRClassLoader.loadClassForName(customizerClassName);
                customizer = (JRChartCustomizer) customizerClass.getDeclaredConstructor().newInstance();
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                    | NoSuchMethodException | InvocationTargetException e) {
                throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_CUSTOMIZER_INSTANCE_ERROR, (Object[]) null, e);
            }

            if (customizer instanceof JRAbstractChartCustomizer) {
                ((JRAbstractChartCustomizer) customizer).init(this);
            }

            if (customizer instanceof NamedChartCustomizer) {
                ((NamedChartCustomizer) customizer).setName(customizerName);
            }
        }

        return customizer;
    }

    @Override
    public void setBand(JRFillBand band) {
        super.setBand(band);

        dataset.setBand(band);
        ((JRFillChartPlot) plot).setBand(band);
    }

    @Override
    protected void evaluateStyle(byte evaluation) throws JRException {
        super.evaluateStyle(evaluation);

        lineBox = null;

        if (providerStyle != null) {
            lineBox = initLineBox.clone(this);
            StyleUtil.appendBox(lineBox, providerStyle.getLineBox());
        }
    }

    @Override
    public ModeEnum getModeValue() {
        return getStyleResolver().getMode(this, ModeEnum.TRANSPARENT);
    }

    @Override
    public Boolean getShowLegend() {
        return ((JRChart) parent).getShowLegend();
    }

    @Override
    public void setShowLegend(Boolean isShowLegend) {
    }

    @Override
    public String getRenderType() {
        return renderType;
    }

    @Override
    public void setRenderType(String renderType) {
    }

    @Override
    public String getTheme() {
        return ((JRChart) parent).getTheme();
    }

    @Override
    public void setTheme(String theme) {
    }

    @Override
    public EvaluationTimeEnum getEvaluationTimeValue() {
        return ((JRChart) parent).getEvaluationTimeValue();
    }

    @Override
    public JRGroup getEvaluationGroup() {
        return evaluationGroup;
    }

    @Override
    public JRLineBox getLineBox() {
        return lineBox == null ? initLineBox : lineBox;
    }

    @Override
    public JRFont getTitleFont() {
        return titleFont;
    }

    @Override
    public EdgeEnum getTitlePositionValue() {
        return ((JRChart) parent).getTitlePositionValue();
    }

    @Override
    public void setTitlePosition(EdgeEnum titlePosition) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Color getTitleColor() {
        return getStyleResolver().getTitleColor(this);
    }

    @Override
    public Color getOwnTitleColor() {
        return ((JRChart) parent).getOwnTitleColor();
    }

    @Override
    public void setTitleColor(Color titleColor) {
    }

    @Override
    public JRFont getSubtitleFont() {
        return subtitleFont;
    }

    @Override
    public Color getOwnSubtitleColor() {
        return ((JRChart) parent).getOwnSubtitleColor();
    }

    @Override
    public Color getSubtitleColor() {
        return getStyleResolver().getSubtitleColor(this);
    }

    @Override
    public void setSubtitleColor(Color subtitleColor) {
    }

    /**
     * Returns the color to use for text in the legend.
     *
     * @return the color to use for text in the legend
     */
    @Override
    public Color getOwnLegendColor() {
        return ((JRChart) parent).getOwnLegendColor();
    }

    /**
     * Returns the inherited color to use for text in the legend.
     *
     * @return the color to use for text in the legend
     */
    @Override
    public Color getLegendColor() {
        return getStyleResolver().getLegendColor(this);
    }

    /**
     * Sets the color to use for text in the legend.
     *
     * @param legendColor the color to use for text in the legend
     */
    @Override
    public void setLegendColor(Color legendColor) {
    }

    /**
     * Returns the color to use as the background of the legend.
     *
     * @return the color to use as the background of the legend
     */
    @Override
    public Color getOwnLegendBackgroundColor() {
        return ((JRChart) parent).getOwnLegendBackgroundColor();
    }

    /**
     * Returns the color to use as the background of the legend.
     *
     * @return the color to use as the background of the legend
     */
    @Override
    public Color getLegendBackgroundColor() {
        return getStyleResolver().getLegendBackgroundColor(this);
    }

    /**
     * Sets the color to use for the background of the legend.
     *
     * @param legendBackgroundColor the color to use for the background of the legend
     */
    @Override
    public void setLegendBackgroundColor(Color legendBackgroundColor) {
    }

    /**
     * Returns the font to use in the legend.
     *
     * @return the font to use in the legend
     */
    @Override
    public JRFont getLegendFont() {
        return legendFont;
    }

    @Override
    public EdgeEnum getLegendPositionValue() {
        return ((JRChart) parent).getLegendPositionValue();
    }

    @Override
    public void setLegendPosition(EdgeEnum legendPosition) {
        throw new UnsupportedOperationException();
    }

    @Override
    public JRExpression getTitleExpression() {
        return ((JRChart) parent).getTitleExpression();
    }

    @Override
    public JRExpression getSubtitleExpression() {
        return ((JRChart) parent).getSubtitleExpression();
    }

    /**
     * @deprecated Replaced by {@link #getHyperlinkTypeValue()}.
     */
    public byte getHyperlinkType() {
        return getHyperlinkTypeValue().getValue();
    }

    @Override
    public HyperlinkTypeEnum getHyperlinkTypeValue() {
        return ((JRChart) parent).getHyperlinkTypeValue();
    }

    @Override
    public byte getHyperlinkTarget() {
        return ((JRChart) parent).getHyperlinkTarget();
    }

    @Override
    public JRExpression getAnchorNameExpression() {
        return ((JRChart) parent).getAnchorNameExpression();
    }

    @Override
    public JRExpression getHyperlinkReferenceExpression() {
        return ((JRChart) parent).getHyperlinkReferenceExpression();
    }

    @Override
    public JRExpression getHyperlinkWhenExpression() {
        return ((JRChart) parent).getHyperlinkWhenExpression();
    }

    @Override
    public JRExpression getHyperlinkAnchorExpression() {
        return ((JRChart) parent).getHyperlinkAnchorExpression();
    }

    @Override
    public JRExpression getHyperlinkPageExpression() {
        return ((JRChart) parent).getHyperlinkPageExpression();
    }

    /**
     *
     */
    public Locale getLocale() {
        return filler.getLocale();
    }

    @Override
    public TimeZone getTimeZone() {
        return super.getTimeZone();
    }

    @Override
    public JRChartDataset getDataset() {
        return dataset;
    }

    /**
     *
     */
    public void setDataset(JRFillChartDataset dataset) {
        this.dataset = dataset;
    }

    @Override
    public JRChartPlot getPlot() {
        return plot;
    }

    /**
     *
     */
    protected Renderable getRenderable() {
        return renderer;
    }

    /**
     *
     */
    protected String getAnchorName() {
        return anchorName;
    }

    /**
     *
     */
    protected String getHyperlinkReference() {
        return hyperlinkReference;
    }

    /**
     *
     */
    protected String getHyperlinkAnchor() {
        return hyperlinkAnchor;
    }

    /**
     *
     */
    protected Integer getHyperlinkPage() {
        return hyperlinkPage;
    }

    protected String getHyperlinkTooltip() {
        return hyperlinkTooltip;
    }

    @Override
    public Color getDefaultLineColor() {
        return getForecolor();
    }

    /**
     *
     */
    protected JRTemplateImage getJRTemplateImage() {
        return (JRTemplateImage) getElementTemplate();
    }

    @Override
    protected JRTemplateElement createElementTemplate() {
        JRTemplateImage templateImage = new JRTemplateImage(getElementOrigin(),
                filler.getJasperPrint().getDefaultStyleProvider(), this);
        templateImage.setUsingCache(false);
        return templateImage;
    }

    @Override
    protected void rewind() {
    }

    @Override
    protected void evaluate(byte evaluation) throws JRException {
        reset();

        evaluatePrintWhenExpression(evaluation);

        if (isPrintWhenExpressionNull() || isPrintWhenTrue()) {
            if (getEvaluationTimeValue() == EvaluationTimeEnum.NOW) {
                evaluateRenderer(evaluation);
            }
        }
    }

    /**
     *
     */
    protected void evaluateRenderer(byte evaluation) throws JRException {
        JFreeChart chart = evaluateChart(evaluation);

        Rectangle2D rectangle = new Rectangle2D.Double(0, 0, getWidth(), getHeight());

        renderer = ChartUtil.getInstance(filler.getJasperReportsContext())
                .getChartRenderableFactory(getRenderType())
                .getRenderable(filler.getJasperReportsContext(), chart, chartHyperlinkProvider, rectangle);
    }

    protected ChartHyperlinkProvider getHyperlinkProvider() {
        return chartHyperlinkProvider;
    }

    /**
     *
     */
    protected JFreeChart evaluateChart(byte evaluation) throws JRException {
        evaluateProperties(evaluation);
        evaluateDatasetRun(evaluation);
        evaluateStyle(evaluation);

        // needs to be lazy loaded here because in the JRFillChart constructor above, the parent properties could return null,
        // as the filler main dataset is not yet set, if the current band is a group band
        if (theme == null) {
            String themeName = getTheme();
            if (themeName == null) {
                themeName = filler.getPropertiesUtil().getProperty(getParentProperties(),
                        JRChart.PROPERTY_CHART_THEME);
            }

            theme = ChartUtil.getInstance(filler.getJasperReportsContext()).getTheme(themeName);
        }

        if (getChartType() == JRChart.CHART_TYPE_MULTI_AXIS) {
            //FIXMECHARTTHEME multi axis charts do not support themes
            createMultiAxisChart(evaluation);
        } else {
            jfreeChart = theme.createChart(new FillChartContext(evaluation));

            chartHyperlinkProvider = createChartHyperlinkProvider();
        }

        for (JRChartCustomizer chartCustomizer : chartCustomizers) {
            chartCustomizer.customize(jfreeChart, this);
        }

        anchorName = (String) evaluateExpression(getAnchorNameExpression(), evaluation);
        hyperlinkReference = (String) evaluateExpression(getHyperlinkReferenceExpression(), evaluation);
        hyperlinkWhen = (Boolean) evaluateExpression(getHyperlinkWhenExpression(), evaluation);
        hyperlinkAnchor = (String) evaluateExpression(getHyperlinkAnchorExpression(), evaluation);
        hyperlinkPage = (Integer) evaluateExpression(getHyperlinkPageExpression(), evaluation);
        hyperlinkTooltip = (String) evaluateExpression(getHyperlinkTooltipExpression(), evaluation);
        hyperlinkParameters = JRFillHyperlinkHelper.evaluateHyperlinkParameters(this, expressionEvaluator,
                evaluation);

        return jfreeChart;
    }

    @Override
    protected boolean prepare(int availableHeight, boolean isOverflow) {
        boolean willOverflow = false;

        if (isPrintWhenExpressionNull() || (!isPrintWhenExpressionNull() && isPrintWhenTrue())) {
            setToPrint(true);
        } else {
            setToPrint(false);
        }

        if (!isToPrint()) {
            return willOverflow;
        }

        boolean isToPrint = true;
        boolean isReprinted = false;

        if (getEvaluationTimeValue() == EvaluationTimeEnum.NOW) {
            if (isOverflow && isAlreadyPrinted() && !isPrintWhenDetailOverflows()) {
                isToPrint = false;
            }

            if (isToPrint && availableHeight < getRelativeY() + getHeight()) {
                isToPrint = false;
                willOverflow = true;
            }

            if (isToPrint && isOverflow &&
            //(this.isAlreadyPrinted() || !this.isPrintRepeatedValues())
                    (isPrintWhenDetailOverflows()
                            && (isAlreadyPrinted() || (!isAlreadyPrinted() && !isPrintRepeatedValues())))) {
                isReprinted = true;
            }

            if (isToPrint && isRemoveLineWhenBlank() && getRenderable() == null) {
                isToPrint = false;
            }
        } else {
            if (isOverflow && isAlreadyPrinted() && !isPrintWhenDetailOverflows()) {
                isToPrint = false;
            }

            if (isToPrint && availableHeight < getRelativeY() + getHeight()) {
                isToPrint = false;
                willOverflow = true;
            }

            if (isToPrint && isOverflow &&
            //(this.isAlreadyPrinted() || !this.isPrintRepeatedValues())
                    (isPrintWhenDetailOverflows()
                            && (isAlreadyPrinted() || (!isAlreadyPrinted() && !isPrintRepeatedValues())))) {
                isReprinted = true;
            }
        }

        setToPrint(isToPrint);
        setReprinted(isReprinted);

        return willOverflow;
    }

    @Override
    protected JRPrintElement fill() {
        JRTemplatePrintImage printImage = new JRTemplatePrintImage(getJRTemplateImage(), printElementOriginator);

        printImage.setUUID(getUUID());
        printImage.setX(getX());
        printImage.setY(getRelativeY());
        printImage.setWidth(getWidth());
        printImage.setHeight(getStretchHeight());
        printImage.setBookmarkLevel(getBookmarkLevel());
        printImage.getPropertiesMap().setProperty(HtmlReportConfiguration.PROPERTY_EMBED_IMAGE,
                Boolean.TRUE.toString());
        printImage.getPropertiesMap().setProperty(HtmlReportConfiguration.PROPERTY_EMBEDDED_SVG_USE_FONTS,
                Boolean.TRUE.toString());

        EvaluationTimeEnum evaluationTime = getEvaluationTimeValue();
        if (evaluationTime == EvaluationTimeEnum.NOW) {
            copy(printImage);
        } else {
            filler.addBoundElement(this, printImage, evaluationTime, getEvaluationGroup(), band);
        }

        return printImage;
    }

    /**
     *
     */
    protected void copy(JRPrintImage printImage) {
        printImage.setRenderer(getRenderable());
        printImage.setAnchorName(getAnchorName());
        if (getHyperlinkWhenExpression() == null || Boolean.TRUE.equals(hyperlinkWhen)) {
            printImage.setHyperlinkReference(getHyperlinkReference());
            printImage.setHyperlinkAnchor(getHyperlinkAnchor());
            printImage.setHyperlinkPage(getHyperlinkPage());
            printImage.setHyperlinkTooltip(getHyperlinkTooltip());
            printImage.setHyperlinkParameters(hyperlinkParameters);
        } else {
            if (printImage instanceof JRTemplatePrintImage)//this is normally the case
            {
                ((JRTemplatePrintImage) printImage).setHyperlinkOmitted(true);
            }

            printImage.setHyperlinkReference(null);
        }
        transferProperties(printImage);
    }

    @Override
    public byte getChartType() {
        return chartType;
    }

    @Override
    public void collectExpressions(JRExpressionCollector collector) {
        collector.collect(this);
    }

    @Override
    public void visit(JRVisitor visitor) {
        visitor.visitChart(this);
    }

    /**
     *
     */
    protected ChartHyperlinkProvider createChartHyperlinkProvider() {
        ChartHyperlinkProvider chartHyperlinkProvider = null;

        switch (getChartType()) {
        case JRChart.CHART_TYPE_AREA:
        case JRChart.CHART_TYPE_BAR:
        case JRChart.CHART_TYPE_BAR3D:
        case JRChart.CHART_TYPE_LINE:
        case JRChart.CHART_TYPE_STACKEDBAR3D:
        case JRChart.CHART_TYPE_STACKEDBAR:
        case JRChart.CHART_TYPE_STACKEDAREA:
            chartHyperlinkProvider = new CategoryChartHyperlinkProvider(
                    ((JRFillCategoryDataset) getDataset()).getItemHyperlinks());
            break;
        case JRChart.CHART_TYPE_BUBBLE:
            chartHyperlinkProvider = new XYChartHyperlinkProvider(
                    ((JRFillXyzDataset) getDataset()).getItemHyperlinks());
            break;
        case JRChart.CHART_TYPE_SCATTER:
        case JRChart.CHART_TYPE_XYAREA:
        case JRChart.CHART_TYPE_XYLINE:
            chartHyperlinkProvider = new XYChartHyperlinkProvider(
                    ((JRFillXyDataset) getDataset()).getItemHyperlinks());
            break;
        case JRChart.CHART_TYPE_CANDLESTICK:
        case JRChart.CHART_TYPE_HIGHLOW:
            chartHyperlinkProvider = new HighLowChartHyperlinkProvider(
                    ((JRFillHighLowDataset) getDataset()).getItemHyperlinks());
            break;
        case JRChart.CHART_TYPE_MULTI_AXIS:
            //multi-axis charts are dealt with in createMultiAxisChart
            break;
        case JRChart.CHART_TYPE_PIE:
        case JRChart.CHART_TYPE_PIE3D:
            chartHyperlinkProvider = new PieChartHyperlinkProvider(
                    ((JRFillPieDataset) getDataset()).getSectionHyperlinks());
            break;
        case JRChart.CHART_TYPE_TIMESERIES:
            chartHyperlinkProvider = new TimeSeriesChartHyperlinkProvider(
                    ((JRFillTimeSeriesDataset) getDataset()).getItemHyperlinks());
            break;
        case JRChart.CHART_TYPE_XYBAR:
            switch (getDataset().getDatasetType()) {
            case JRChartDataset.TIMESERIES_DATASET:
                chartHyperlinkProvider = new TimeSeriesChartHyperlinkProvider(
                        ((JRFillTimeSeriesDataset) getDataset()).getItemHyperlinks());
                break;
            case JRChartDataset.TIMEPERIOD_DATASET:
                chartHyperlinkProvider = new TimePeriodChartHyperlinkProvider(
                        ((JRFillTimePeriodDataset) getDataset()).getItemHyperlinks());
                break;
            case JRChartDataset.XY_DATASET:
                chartHyperlinkProvider = new XYChartHyperlinkProvider(
                        ((JRFillXyDataset) getDataset()).getItemHyperlinks());
                break;
            default:
                break;
            }
            break;
        case JRChart.CHART_TYPE_GANTT:
            chartHyperlinkProvider = new XYChartHyperlinkProvider(
                    ((JRFillGanttDataset) getDataset()).getItemHyperlinks());
            break;
        case JRChart.CHART_TYPE_METER:
        case JRChart.CHART_TYPE_THERMOMETER:
            //no item hyperlinks
            break;
        default:
            throw new JRRuntimeException(JRBaseChart.EXCEPTION_MESSAGE_KEY_CHART_TYPE_NOT_SUPPORTED,
                    new Object[] { getChartType() });
        }

        return chartHyperlinkProvider;
    }

    /**
     * Build and configure a multiple axis chart.  A multiple axis chart support more than
     * one range axis.  Multiple datasets using different ranges can be displayed as long as
     * they share a common domain axis.  Each dataset can be rendered differently, so one chart
     * can contain (for example) two line charts, a bar chart and an area chart.
     * <br><br>
     * Multiple axis charts are handled differently than the other chart types.  They do not
     * have a dataset, as each chart that is added to the multiple axis chart has its own
     * dataset.  For simplicity, each dataset is treated as its own chart, and in fact we
     * reuse the design of all the chart types and let JFreeChart actually run them.  Then
     * we pull out the bits we need and add it to the common chart.  All the plot and chart
     * options on the nested charts is ignored, and all formatting is controlled by the plot
     * attached to the multiAxisChart.  The one exception is seriesColor, which can be used in
     * a nested report to specify a color for a specific series in that report.
     *
     * @param evaluation current expression evaluation phase
     * @throws JRException
     */
    protected void createMultiAxisChart(byte evaluation) throws JRException {
        // A multi axis chart has to have at least one axis and chart specified.
        // Create the first axis as the base plot, and then go ahead and create the
        // charts for any additional axes.  Just take the renderer and data series
        // from those charts and add them to the first one.
        Plot mainPlot = null;

        JRFillMultiAxisPlot jrPlot = (JRFillMultiAxisPlot) getPlot();

        // create a multi axis hyperlink provider
        MultiAxisChartHyperlinkProvider multiHyperlinkProvider = new MultiAxisChartHyperlinkProvider();

        // Generate the main plot from the first axes specified.
        Iterator<JRChartAxis> iter = jrPlot.getAxes().iterator();
        if (iter.hasNext()) {
            JRFillChartAxis axis = (JRFillChartAxis) iter.next();
            JRFillChart fillChart = axis.getFillChart();

            //a JFreeChart object should be obtained first; the rendering type should be always "vector"

            jfreeChart = fillChart.evaluateChart(evaluation);
            //FIXME honor printWhenExpression
            // Override the plot from the first axis with the plot for the multi-axis
            // chart.
            //FIXME is the above comment true? 
            //configureChart(jfreeChart, getPlot(), evaluation);
            mainPlot = jfreeChart.getPlot();
            ChartHyperlinkProvider axisHyperlinkProvider = fillChart.getHyperlinkProvider();

            if (mainPlot instanceof CategoryPlot) {
                CategoryPlot categoryPlot = (CategoryPlot) mainPlot;
                categoryPlot.setRangeAxisLocation(0, getChartAxisLocation(axis));
                if (axisHyperlinkProvider != null) {
                    multiHyperlinkProvider.addHyperlinkProvider(categoryPlot.getDataset(), axisHyperlinkProvider);
                }
            } else if (mainPlot instanceof XYPlot) {
                XYPlot xyPlot = (XYPlot) mainPlot;
                xyPlot.setRangeAxisLocation(0, getChartAxisLocation(axis));
                if (axisHyperlinkProvider != null) {
                    multiHyperlinkProvider.addHyperlinkProvider(xyPlot.getDataset(), axisHyperlinkProvider);
                }
            }
        }

        // Now handle all the extra axes, if any.
        int axisNumber = 0;
        while (iter.hasNext()) {
            JRFillChartAxis chartAxis = (JRFillChartAxis) iter.next();
            JRFillChart fillChart = chartAxis.getFillChart();

            fillChart.evaluatePrintWhenExpression(evaluation);
            if (!(fillChart.isPrintWhenExpressionNull() || fillChart.isPrintWhenTrue())) {
                continue;
            }

            axisNumber++;
            JFreeChart axisChart = fillChart.evaluateChart(evaluation);
            ChartHyperlinkProvider axisHyperlinkProvider = fillChart.getHyperlinkProvider();

            // In JFreeChart to add a second chart type to an existing chart
            // you need to add an axis, a data series and a renderer.  To
            // leverage existing code we simply create a new chart for the
            // axis and then pull out the bits we need and add them to the multi
            // chart.  Currently JFree only supports category plots and xy plots
            // in a multi-axis chart, and you can not mix the two.
            if (mainPlot instanceof CategoryPlot) {
                CategoryPlot mainCatPlot = (CategoryPlot) mainPlot;
                if (!(axisChart.getPlot() instanceof CategoryPlot)) {
                    throw new JRException(EXCEPTION_MESSAGE_KEY_MULTIAXIS_PLOT_TYPES_MIX_NOT_ALLOWED,
                            (Object[]) null);
                }

                // Get the axis and add it to the multi axis chart plot
                CategoryPlot axisPlot = (CategoryPlot) axisChart.getPlot();
                mainCatPlot.setRangeAxis(axisNumber, axisPlot.getRangeAxis());
                mainCatPlot.setRangeAxisLocation(axisNumber, getChartAxisLocation(chartAxis));

                // Add the data set and map it to the recently added axis
                mainCatPlot.setDataset(axisNumber, axisPlot.getDataset());
                mainCatPlot.mapDatasetToRangeAxis(axisNumber, axisNumber);

                // Set the renderer to use to draw the dataset.
                mainCatPlot.setRenderer(axisNumber, axisPlot.getRenderer());

                // Handle any color series for this chart
                configureAxisSeriesColors(axisPlot.getRenderer(), fillChart.getPlot());

                if (axisHyperlinkProvider != null) {
                    multiHyperlinkProvider.addHyperlinkProvider(axisPlot.getDataset(), axisHyperlinkProvider);
                }
            } else if (mainPlot instanceof XYPlot) {
                XYPlot mainXyPlot = (XYPlot) mainPlot;
                if (!(axisChart.getPlot() instanceof XYPlot)) {
                    throw new JRException(EXCEPTION_MESSAGE_KEY_MULTIAXIS_PLOT_TYPES_MIX_NOT_ALLOWED,
                            (Object[]) null);
                }

                // Get the axis and add it to the multi axis chart plot
                XYPlot axisPlot = (XYPlot) axisChart.getPlot();
                mainXyPlot.setRangeAxis(axisNumber, axisPlot.getRangeAxis());
                mainXyPlot.setRangeAxisLocation(axisNumber, getChartAxisLocation(chartAxis));

                // Add the data set and map it to the recently added axis
                mainXyPlot.setDataset(axisNumber, axisPlot.getDataset());
                mainXyPlot.mapDatasetToRangeAxis(axisNumber, axisNumber);

                // Set the renderer to use to draw the dataset.
                mainXyPlot.setRenderer(axisNumber, axisPlot.getRenderer());

                // Handle any color series for this chart
                configureAxisSeriesColors(axisPlot.getRenderer(), fillChart.getPlot());

                if (axisHyperlinkProvider != null) {
                    multiHyperlinkProvider.addHyperlinkProvider(axisPlot.getDataset(), axisHyperlinkProvider);
                }
            } else {
                throw new JRException(EXCEPTION_MESSAGE_KEY_MULTIAXIS_PLOT_NOT_SUPPORTED, (Object[]) null);
            }
        }

        //set the multi hyperlink provider
        chartHyperlinkProvider = multiHyperlinkProvider;
    }

    /**
     * The series colors set in the main plot of a multiple axis chart are used for
     * all the rendered charts in the plot.  This is a problem with multiple line
     * charts, using different scales and thus different axis.  All the lines will
     * be drawn using the first series color (since they are the first series for that
     * rendered) and it will be impossible to tell them apart.
     * <br><br>
     * For this reason we interpret series colors for charts included in a multiple
     * axis chart as specify absolute series colors for that renderer.
     *
     * @param renderer the renderer of the chart being created
     * @param jrPlot the Jasper view of that plot
     */
    private void configureAxisSeriesColors(CategoryItemRenderer renderer, JRChartPlot jrPlot) {
        SortedSet<JRSeriesColor> seriesColors = jrPlot.getSeriesColors();

        if (seriesColors != null) {
            Iterator<JRSeriesColor> iter = seriesColors.iterator();
            while (iter.hasNext()) {
                JRSeriesColor seriesColor = iter.next();
                renderer.setSeriesPaint(seriesColor.getSeriesOrder(), seriesColor.getColor());
            }
        }
    }

    /**
     * The series colors set in the main plot of a multiple axis chart are used for
     * all the rendered charts in the plot.  This is a problem with multiple line
     * charts, using different scales and thus different axis.  All the lines will
     * be drawn using the first series color (since they are the first series for that
     * rendered) and it will be impossible to tell them apart.
     * <br>
     * For this reason we interpret series colors for charts included in a multiple
     * axis chart as specify absolute series colors for that renderer.
     *
     * @param renderer the renderer of the chart being created
     * @param jrPlot the Jasper view of that plot
     */
    private void configureAxisSeriesColors(XYItemRenderer renderer, JRChartPlot jrPlot) {
        SortedSet<JRSeriesColor> seriesColors = jrPlot.getSeriesColors();

        if (seriesColors != null) {
            Iterator<JRSeriesColor> iter = seriesColors.iterator();
            while (iter.hasNext()) {
                JRSeriesColor seriesColor = iter.next();
                renderer.setSeriesPaint(seriesColor.getSeriesOrder(), seriesColor.getColor());
            }
        }
    }

    /**
     * Converts a JasperReport data range into one understood by JFreeChart.
     *
     * @param dataRange the JasperReport version of the range
     * @param evaluation current expression evaluation phase
     * @return the JFreeChart version of the range
     * @throws JRException thrown when the low value of the range is greater than the
     *                   high value
     */
    protected Range convertRange(JRDataRange dataRange, byte evaluation) throws JRException {
        if (dataRange == null) {
            return null;
        }
        Number low = (Number) evaluateExpression(dataRange.getLowExpression(), evaluation);
        Number high = (Number) evaluateExpression(dataRange.getHighExpression(), evaluation);
        return new Range(low != null ? low.doubleValue() : 0.0, high != null ? high.doubleValue() : 100.0);
    }

    /**
     * Converts a JasperReports meter interval to one that JFreeChart understands.
     *
     * @param interval the JasperReports definition of an interval
     * @param evaluation current evaluation time
     * @return the JFreeChart version of the same interval
     * @throws JRException thrown when the interval contains an invalid range
     */
    protected MeterInterval convertInterval(JRMeterInterval interval, byte evaluation) throws JRException {
        String label = interval.getLabel();
        if (label == null) {
            label = "";
        }

        Range range = convertRange(interval.getDataRange(), evaluation);

        Color color = interval.getBackgroundColor();
        float[] components = color.getRGBColorComponents(null);
        float alpha = (float) (interval.getAlphaDouble() == null ? JRMeterInterval.DEFAULT_TRANSPARENCY
                : interval.getAlphaDouble());

        Color alphaColor = new Color(components[0], components[1], components[2], alpha);

        return new MeterInterval(label, range, alphaColor, null, alphaColor);
    }

    protected AxisLocation getChartAxisLocation(JRFillChartAxis chartAxis) {
        return chartAxis.getPositionValue() != null
                && chartAxis.getPositionValue() == AxisPositionEnum.RIGHT_OR_BOTTOM ? AxisLocation.BOTTOM_OR_RIGHT
                        : AxisLocation.TOP_OR_LEFT;
    }

    @Override
    protected void resolveElement(JRPrintElement element, byte evaluation) throws JRException {
        evaluateRenderer(evaluation);

        copy((JRPrintImage) element);
        filler.updateBookmark(element);
    }

    @Override
    public int getBookmarkLevel() {
        return ((JRChart) parent).getBookmarkLevel();
    }

    @Override
    public String getCustomizerClass() {
        return ((JRChart) parent).getCustomizerClass();
    }

    private void evaluateDatasetRun(byte evaluation) throws JRException {
        dataset.evaluateDatasetRun(evaluation);
    }

    @Override
    public JRFillCloneable createClone(JRFillCloneFactory factory) {
        //not needed
        return null;
    }

    @Override
    public JRHyperlinkParameter[] getHyperlinkParameters() {
        return ((JRChart) parent).getHyperlinkParameters();
    }

    @Override
    public String getLinkType() {
        return ((JRChart) parent).getLinkType();
    }

    @Override
    public String getLinkTarget() {
        return ((JRChart) parent).getLinkTarget();
    }

    @Override
    public JRExpression getHyperlinkTooltipExpression() {
        return ((JRChart) parent).getHyperlinkTooltipExpression();
    }

    class FillChartContext implements ChartContext {
        private final byte evaluation;

        protected FillChartContext(byte evaluation) {
            this.evaluation = evaluation;

            JRFillChart.this.filler.getJasperReportsContext();
        }

        @Override
        public JasperReportsContext getJasperReportsContext() {
            return JRFillChart.this.filler.getJasperReportsContext();
        }

        @Override
        public String evaluateTextExpression(JRExpression expression) throws JRException {
            return JRStringUtil.getString(JRFillChart.this.evaluateExpression(expression, evaluation));
        }

        @Override
        public Object evaluateExpression(JRExpression expression) throws JRException {
            return JRFillChart.this.evaluateExpression(expression, evaluation);
        }

        @Override
        public JRChart getChart() {
            return JRFillChart.this;
        }

        @Override
        public Dataset getDataset() {
            return ((JRFillChartDataset) JRFillChart.this.getDataset()).getDataset();
        }

        @Override
        public Object getLabelGenerator() {
            return ((JRFillChartDataset) JRFillChart.this.getDataset()).getLabelGenerator();
        }

        @Override
        public Locale getLocale() {
            return JRFillChart.this.getLocale();
        }

        @Override
        public TimeZone getTimeZone() {
            return JRFillChart.this.getTimeZone();
        }
    }
}