view gnv-artifacts/src/main/java/de/intevation/gnv/chart/XMLChartTheme.java @ 774:d0a39efbfd96

Removed race-condition when trying to access the index of the next line color used in charts (applied patch in issue211). gnv-artifacts/trunk@834 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Fri, 26 Mar 2010 10:06:36 +0000
parents 79401c871da4
children 9a828e5a2390
line wrap: on
line source
package de.intevation.gnv.chart;

import java.awt.Font;
import java.awt.Color;
import java.awt.Paint;
import java.awt.geom.Ellipse2D;
import java.lang.NumberFormatException;

import org.apache.log4j.Logger;

import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.ui.RectangleInsets;
import org.jfree.util.UnitType;


import org.w3c.dom.Document;

import de.intevation.artifactdatabase.Config;

/**
 * Implementation of JFreeChart's default implementation
 * <code>StandardChartTheme</code>. This class takes an xml document with a
 * bunch of parameters and turns it into a <code>ChartTheme</code> to change 
 * the appearance of charts.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class XMLChartTheme
extends      StandardChartTheme
{
    /**
     * Default color.
     */
    private static final Color DEFAULT_COLOR = Color.BLACK;

    /**
     * Logger used for logging with log4j.
     */
    private Logger log = Logger.getLogger(XMLChartTheme.class);

    /**
     * Field storing the visibility of the domain crosshair
     */
    protected boolean domainCrosshairVisible;

    /**
     * Field storing the visibility of the range crosshair
     */
    protected boolean rangeCrosshairVisible;
    
    /**
     * Field storing the visiblity of lines.
     */
    protected boolean renderLines;

    /**
     * Field storing the visibility of data points
     */
    protected boolean renderShapes;

    /**
     * Field storing the width of a data point
     */
    protected int pointWidth;

    /**
     * Field storing the height of a data point.
     */
    protected int pointHeight;

    /**
     * Field storing the base color of a bin in a histogram chart
     */
    protected Paint histogramBasePaint;


    /**
     * Constructor
     *
     * @param name Name for this theme.
     */
    public XMLChartTheme(String name) {
        super(name);
    }


    /**
     * Setter method for the visibility of the domain crosshair.
     *
     * @param visible True, if domain crosshair should be visible
     */
    public void setDomainCrosshairVisible(boolean visible) {
        this.domainCrosshairVisible = visible;
    }


    /**
     * Getter method for retrieving the visibility of the domain crosshair.
     *
     * @return Visibility of the domain crosshair.
     */
    public boolean getDomainCrosshairVisible() {
        return domainCrosshairVisible;
    }


    /**
     * Getter method for retrieving the visibility of the range crosshair.
     *
     * @return Visibility of the range crosshair.
     */
    public boolean getRangeCrosshairVisible() {
        return rangeCrosshairVisible;
    }


    /**
     * Setter method for the visibility of the range crosshair.
     *
     * @param visible True, if range crosshair should be visible
     */
    public void setRangeCrosshairVisible(boolean visible) {
        this.rangeCrosshairVisible = visible;
    }


    /**
     * Method to set the bin color of histograms.
     *
     * @param c Bin color
     */
    public void setHistogramBasePaint(Color c) {
        this.histogramBasePaint = c;
    }


    /**
     * Getter method for retrieving the bin color.
     *
     * @return Bin color
     */
    public Paint getHistogramBasePaint() {
        return histogramBasePaint;
    }


    /**
     * Take a given xml document and read the configuration of a chart
     * appearance.
     *
     * @param document XML document
     */
    public void applyXMLConfiguration(Document document) {
        log.debug("create XMLChartTheme");

        init(document);
    }


    /**
     * Start parsing the different settings from <code>document</code>.
     *
     * @param document XML document
     */
    private void init(Document document) {
        log.debug("init XMLChartTheme parameters");

        initChartParameters(document);
        initTitleParameters(document);
        initSubtitleParameters(document);
        initPlotParameters(document);
        initAxisParameters(document);
        initLegendParameters(document);
        initRenderer(document);
        initHistogramColor(document);
    }


    /**
     * Read parameters configuring the title of a chart.
     *
     * @param document XML document
     */
    private void initTitleParameters(Document document) {
        log.debug("init title parameters.");

        int     size  = getInt(document, "theme/title/font/size/@value");
        String  type  = getString(document, "theme/title/font/type/@value");
        boolean bold  = getBool(document, "theme/title/font/bold/@value");

        setExtraLargeFont(createFont(type, size, bold));

        String color = getString(document, "theme/title/font/color/@value");
        Color  c     = decodeColor(color);
        if (c != null)
            setTitlePaint(c);
    }


    /**
     * Read parameters configuring the subtitle of a chart.
     *
     * @param document XML document
     */
    private void initSubtitleParameters(Document document) {
        log.debug("init title parameters.");

        int     size  = getInt(document, "theme/subtitle/font/size/@value");
        String  type  = getString(document, "theme/subtitle/font/type/@value");
        boolean bold  = getBool(document, "theme/subtitle/font/bold/@value");

        setLargeFont(createFont(type, size, bold));

        String col  = getString(document, "theme/subtitle/font/color/@value");
        setSubtitlePaint(Color.decode(col));
    }


    /**
     * Read parameters configuring the background color of a chart.
     *
     * @param document XML document
     */
    private void initChartParameters(Document document) {
        log.debug("init chart parameters.");

        String bg = getString(document, "theme/chart/background/color/@value");
        Color  c  = decodeColor(bg);
        if (c != null)
            setChartBackgroundPaint(c);
    }


    /**
     * Read parameters configuring the plot of a chart.
     *
     * @param document XML document
     */
    private void initPlotParameters(Document document) {
        log.debug("init plot parameters.");

        String tmp = null;
        tmp        = getString(document, "theme/plot/background/color/@value");
        Color  c   = decodeColor(tmp);
        if (c != null)
            setPlotBackgroundPaint(c);

        tmp = getString(document, "theme/plot/outline/color/@value");
        c   = decodeColor(tmp);
        if (c != null)
            setPlotOutlinePaint(c);

        tmp = getString(document, "theme/plot/domaingridline/color/@value");
        c   = decodeColor(tmp);
        if (c != null)
            setDomainGridlinePaint(c);

        tmp = getString(document, "theme/plot/rangegridline/color/@value");
        c   = decodeColor(tmp);
        if (c != null)
            setRangeGridlinePaint(c);

        boolean rangeCrosshairVisible = getBool(
            document, "theme/plot/rangecrosshair/visible/@value");
        setRangeCrosshairVisible(rangeCrosshairVisible);

        boolean domainCrosshairVisible = getBool(
            document, "theme/plot/domaincrosshair/visible/@value");
        setDomainCrosshairVisible(domainCrosshairVisible);

        int top    = getInt(document, "theme/plot/offset/top/@value");
        int bottom = getInt(document, "theme/plot/offset/bottom/@value");
        int left   = getInt(document, "theme/plot/offset/left/@value");
        int right  = getInt(document, "theme/plot/offset/right/@value");
        setAxisOffset(new RectangleInsets(
            UnitType.RELATIVE,
            top, left, bottom, right)
        );
    }


    /**
     * Read parameters configuring the axes of a plot.
     *
     * @param document XML document
     */
    private void initAxisParameters(Document document) {
        log.debug("init axis parameters.");

        String tmp = null;
        tmp        = getString(document, "theme/axis/label/color/@value");
        Color c    = decodeColor(tmp);
        if (c != null)
            setAxisLabelPaint(c);

        tmp = getString(document, "theme/axis/ticklabel/color/@value");
        c   = decodeColor(tmp);
        if (c != null)
            setTickLabelPaint(c);
    }


    /**
     * Read parameters configuring the legend of a chart.
     *
     * @param document XML document
     */
    private void initLegendParameters(Document document) {
        log.debug("init legend parameters.");

        String tmp = null;
        tmp        = getString(document, "theme/legend/font/color/@value");
        Color c    = decodeColor(tmp);
        if (c != null)
            setLegendItemPaint(c);

        tmp = getString(document, "theme/legend/background/color/@value");
        c   = decodeColor(tmp);
        if (c != null)
            setLegendBackgroundPaint(c);
    }


    /**
     * Read parameters configuring the renderer of a plot.
     *
     * @param document XML document
     */
    private void initRenderer(Document document) {
        log.debug("init renderer parameters.");

        pointWidth  = getInt(document, "theme/plot/itemrenderer/width/@value");
        log.debug("Read point width of " + pointWidth);
        pointHeight = getInt(document, "theme/plot/itemrenderer/height/@value");
        log.debug("Read point height of " + pointHeight);
        renderLines = getBool(
            document, "theme/plot/itemrenderer/renderLines/@value"
        );
        renderShapes = getBool(
            document, "theme/plot/itemrenderer/renderPoints/@value"
        );
    }


    /**
     * Read base color of bins in histogram charts.
     *
     * @param document XML document
     */
    private void initHistogramColor(Document document) {
        log.debug("init histogram color");
        String tmp = getString(document, "theme/histogram/bar/color/@value");
        Color c = decodeColor(tmp);

        if (c != null)
            setHistogramBasePaint(c);
    }


    /**
     * Read a xpath expression and return the matched string.
     *
     * @param document Document
     * @param xpath XPath expression
     *
     * @return Matched string
     */
    private static String getString(Document document, String xpath) {
        return Config.getStringXPath(document, xpath);
    }


    /**
     * Read a xpath and turn it into an integer.
     *
     * @param document Document
     * @param xpath XPath expression
     *
     * @return Matched string as integer representation. Return 0 if no integer
     * have been found at <code>xpath</code>.
     */
    private static int getInt(Document document, String xpath) {
        String tmp = getString(document, xpath);

        if (tmp != null)
            return Integer.parseInt(tmp);
        else
            return 0;
    }


    /**
     * Read a xpath and turn it into a boolean.
     *
     * @param document Document
     * @param xpath XPath expression
     *
     * @return Matched string as boolean representation. Return false if no
     * boolean have been found at <code>xpath</code>.
     */
    private static boolean getBool(Document document, String xpath) {
        String tmp = getString(document, xpath);

        if (tmp != null)
            return Boolean.parseBoolean(tmp);
        else
            return false;
    }


    /**
     * Turns a string into a color using {@link java.awt.Color}.
     *
     * @param Color as string
     *
     * @return Color
     */
    protected Color decodeColor(String color) {
        try {
            if (color == null)
                return null;

            return Color.decode(color);
        }
        catch (NumberFormatException nfe) {
            log.warn("Error while parsing color: " + color, nfe);
        }

        return null;
    }


    /**
     * Create a font with the given parameters.
     *
     * @param type Font type
     * @param size Font size
     * @param bold Font weight
     *
     * @return Font
     */
    protected Font createFont(String type, int size, boolean bold) {
        Font font = null;
        if (bold)
            font = new Font(type, Font.BOLD, size);
        else
            font = new Font(type, Font.PLAIN, size);

        return font;
    }


    /**
     * Apply settings of this <code>ChartTheme</code> to the given
     * <code>XYPlot</code>.
     *
     * @param plot XYPlot
     */
    protected void applyToXYPlot(XYPlot plot) {
        log.debug("apply theme parameter to XYPlot");

        super.applyToXYPlot(plot);
        plot.setDomainCrosshairVisible(this.domainCrosshairVisible);
        plot.setRangeCrosshairVisible(this.rangeCrosshairVisible);

        AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) 
            plot.getRenderer();

        if (renderer instanceof XYLineAndShapeRenderer)
            applyToXYLineAndShapeRenderer(plot);

        if (renderer instanceof XYBarRenderer)
            applyToXYBarRenderer(plot);
    }


    /**
     * Apply settings of this <code>ChartTheme</code> to the
     * <code>XYLineAndShapeRenderer</code> of the given <code>XYPlot</code>.
     *
     * @param plot XYPlot
     */
    protected void applyToXYLineAndShapeRenderer(XYPlot plot) {
        if (plot == null)
            return;

        XYLineAndShapeRenderer renderer =
            (XYLineAndShapeRenderer) plot.getRenderer();

        Ellipse2D.Double point = new Ellipse2D.Double(
            -(pointWidth/2), -(pointHeight/2), pointWidth, pointHeight
        );

        renderer.setSeriesShape(0, point);
        renderer.setSeriesShapesVisible(0, renderShapes);
        renderer.setSeriesLinesVisible(0, renderLines);

        plot.setRenderer(renderer);
    }


    /**
     * Apply settings of this <code>ChartTheme</code> to the
     * <code>XYBarRenderer</code> of the given <code>XYPlot</code>.
     *
     * @param plot XYPlot
     */
    protected void applyToXYBarRenderer(XYPlot plot) {
        if (plot == null)
            return;

        XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer();

        renderer.setSeriesPaint(0, histogramBasePaint);
    }
}

http://dive4elements.wald.intevation.org