ca.sqlpower.wabit.report.ChartRenderer.java Source code

Java tutorial

Introduction

Here is the source code for ca.sqlpower.wabit.report.ChartRenderer.java

Source

/*
 * Copyright (c) 2009, SQL Power Group Inc.
 *
 * This file is part of Wabit.
 *
 * Wabit is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Wabit 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */

package ca.sqlpower.wabit.report;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.List;

import javax.annotation.Nonnull;

import org.apache.log4j.Logger;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;

import ca.sqlpower.object.AbstractSPListener;
import ca.sqlpower.object.CleanupExceptions;
import ca.sqlpower.object.SPObject;
import ca.sqlpower.object.SPVariableResolver;
import ca.sqlpower.wabit.AbstractWabitObject;
import ca.sqlpower.wabit.WabitObject;
import ca.sqlpower.wabit.report.chart.Chart;
import ca.sqlpower.wabit.report.chart.ChartDataChangedEvent;
import ca.sqlpower.wabit.report.chart.ChartDataListener;
import ca.sqlpower.wabit.report.chart.ChartGradientPainter;
import ca.sqlpower.wabit.report.selectors.ContextAware;
import ca.sqlpower.wabit.swingui.chart.ChartSwingUtil;

/**
 * This class will render a chart from a query's result set in a chart format
 * defined by the user.
 */
public class ChartRenderer extends AbstractWabitObject implements WabitObjectReportRenderer, ContextAware {

    private static final Logger logger = Logger.getLogger(ChartRenderer.class);
    private boolean needsRefresh = false;

    private final Chart chart;

    private Chart chartCache;

    private final ChartDataListener chartListener = new ChartDataListener() {
        public void chartDataChanged(ChartDataChangedEvent evt) {
            if (getParent() != null) {
                getParent().repaint();
            }
        }
    };

    private final AbstractSPListener chartStructureListener = new AbstractSPListener() {

        protected void propertyChangeImpl(java.beans.PropertyChangeEvent evt) {
            needsRefresh = true;
        };
    };

    public ChartRenderer(@Nonnull ChartRenderer renderer) {
        this(renderer.chart);
    }

    public ChartRenderer(@Nonnull Chart chart) {
        if (chart == null) {
            throw new NullPointerException("Null chart not permitted");
        }
        /*
         * Because charts are mutable objects and cannot work in multi-threaded
         * way, we have to grab a copy of the chart source and update it every time
         * the source one changes.
         */
        this.chart = chart;
        this.chart.addSPListener(chartStructureListener);
        setName("Renderer of: " + chart.getName());

        needsRefresh = true;
    }

    public Color getBackgroundColour() {
        return chart.getBackgroundColour();
    }

    public @Nonnull Chart getContent() {
        return chart;
    }

    // TODO we intend to remove this whole method into the SwingUI layer (SwingContentRenderer)
    public boolean renderReportContent(Graphics2D g, double width, double height, double scaleFactor, int pageIndex,
            boolean printing, SPVariableResolver variablesContext) {

        if (printing) {
            // If we're printing a streaming query, we have to
            // print whatever's displayed.
            if (this.chartCache == null || (this.chartCache != null && !this.chartCache.getQuery().isStreaming())) {
                refresh(false);
            }
        } else if (needsRefresh || this.chartCache == null) {
            // No chart loaded. Doing a refresh will trigger a new 
            // redraw later on.
            refresh();
            return false;
        }

        JFreeChart jFreeChart = null;
        try {
            jFreeChart = ChartSwingUtil.createChartFromQuery(chartCache);
            if (jFreeChart == null) {
                g.drawString("Loading...", 0, g.getFontMetrics().getHeight());
                return false;
            }

            Rectangle2D area = new Rectangle2D.Double(0, 0, width, height);

            // first pass establishes rendering info but draws nothing
            ChartRenderingInfo info = new ChartRenderingInfo();
            Graphics2D dummyGraphics = (Graphics2D) g.create(0, 0, 0, 0);
            jFreeChart.draw(dummyGraphics, area, info);
            dummyGraphics.dispose();

            // now for real
            Rectangle2D plotArea = info.getPlotInfo().getDataArea();
            ChartGradientPainter.paintChartGradient(g, area, (int) plotArea.getMaxY());
            jFreeChart.draw(g, area);

        } catch (Exception e) {
            logger.error("Error while rendering chart", e);
            g.drawString("Could not render chart: " + e.getMessage(), 0, g.getFontMetrics().getHeight());
        }
        return false;
    }

    public void resetToFirstPage() {
        //do nothing.
    }

    public boolean allowsChildren() {
        return false;
    }

    public int childPositionOffset(Class<? extends SPObject> childType) {
        return 0;
    }

    public List<? extends WabitObject> getChildren() {
        return Collections.emptyList();
    }

    public List<WabitObject> getDependencies() {
        return Collections.singletonList((WabitObject) chart);
    }

    public void removeDependency(SPObject dependency) {
        ((ContentBox) getParent()).setContentRenderer(null);
    }

    @Override
    public CleanupExceptions cleanup() {
        if (this.chartCache != null) {
            this.chartCache.removeChartDataListener(chartListener);
            this.chartCache.cleanup();
        }
        this.chart.removeSPListener(chartStructureListener);
        return new CleanupExceptions();
    }

    public void refresh() {
        refresh(true);
    }

    public void refresh(boolean async) {
        if (this.chartCache != null) {
            this.chartCache.removeChartDataListener(chartListener);
            this.chartCache.cleanup();
        }
        this.chartCache = new Chart(ChartRenderer.this.chart, this);
        this.chartCache.addChartDataListener(chartListener);
        this.chartCache.refresh(async);
        needsRefresh = false;
    }

    @Override
    protected boolean removeChildImpl(SPObject child) {
        return false;
    }

    @Override
    public ContentBox getParent() {
        return (ContentBox) super.getParent();
    }

    public List<Class<? extends SPObject>> getAllowedChildTypes() {
        return Collections.emptyList();
    }
}