ingo@1115: /*
ingo@1115:  * Copyright (c) 2010 by Intevation GmbH
ingo@1115:  *
ingo@1115:  * This program is free software under the LGPL (>=v2.1)
ingo@1115:  * Read the file LGPL.txt coming with the software for details
ingo@1115:  * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@1115:  */
ingo@1115: 
ingo@304: package de.intevation.gnv.chart;
ingo@304: 
sascha@1117: import de.intevation.artifacts.common.utils.Config;
sascha@779: 
ingo@304: import java.awt.Color;
sascha@779: import java.awt.Font;
ingo@617: import java.awt.Paint;
sascha@779: 
ingo@327: import java.awt.geom.Ellipse2D;
sascha@779: 
ingo@304: import org.apache.log4j.Logger;
ingo@304: 
ingo@304: import org.jfree.chart.StandardChartTheme;
sascha@779: 
ingo@304: import org.jfree.chart.plot.XYPlot;
sascha@779: 
ingo@617: import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
ingo@617: import org.jfree.chart.renderer.xy.XYBarRenderer;
ingo@327: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
sascha@779: 
ingo@304: import org.jfree.ui.RectangleInsets;
sascha@779: 
ingo@304: import org.jfree.util.UnitType;
ingo@304: 
ingo@304: import org.w3c.dom.Document;
ingo@304: 
ingo@304: /**
ingo@767:  * Implementation of JFreeChart's default implementation
ingo@767:  * <code>StandardChartTheme</code>. This class takes an xml document with a
sascha@778:  * bunch of parameters and turns it into a <code>ChartTheme</code> to change
ingo@767:  * the appearance of charts.
ingo@767:  *
ingo@767:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@304:  */
ingo@304: public class XMLChartTheme
ingo@304: extends      StandardChartTheme
ingo@304: {
ingo@767:     /**
ingo@767:      * Default color.
ingo@767:      */
ingo@304:     private static final Color DEFAULT_COLOR = Color.BLACK;
ingo@304: 
ingo@767:     /**
ingo@767:      * Logger used for logging with log4j.
ingo@767:      */
ingo@304:     private Logger log = Logger.getLogger(XMLChartTheme.class);
ingo@304: 
ingo@767:     /**
ingo@767:      * Field storing the visibility of the domain crosshair
ingo@767:      */
ingo@304:     protected boolean domainCrosshairVisible;
ingo@767: 
ingo@767:     /**
ingo@767:      * Field storing the visibility of the range crosshair
ingo@767:      */
ingo@304:     protected boolean rangeCrosshairVisible;
sascha@778: 
ingo@767:     /**
ingo@767:      * Field storing the visiblity of lines.
ingo@767:      */
ingo@327:     protected boolean renderLines;
ingo@767: 
ingo@767:     /**
ingo@767:      * Field storing the visibility of data points
ingo@767:      */
ingo@327:     protected boolean renderShapes;
ingo@327: 
ingo@767:     /**
ingo@767:      * Field storing the width of a data point
ingo@767:      */
ingo@327:     protected int pointWidth;
ingo@767: 
ingo@767:     /**
ingo@767:      * Field storing the height of a data point.
ingo@767:      */
ingo@327:     protected int pointHeight;
ingo@304: 
ingo@767:     /**
ingo@767:      * Field storing the base color of a bin in a histogram chart
ingo@767:      */
ingo@617:     protected Paint histogramBasePaint;
ingo@617: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Constructor
ingo@767:      *
ingo@767:      * @param name Name for this theme.
ingo@767:      */
ingo@304:     public XMLChartTheme(String name) {
ingo@304:         super(name);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Setter method for the visibility of the domain crosshair.
ingo@767:      *
ingo@767:      * @param visible True, if domain crosshair should be visible
ingo@767:      */
ingo@304:     public void setDomainCrosshairVisible(boolean visible) {
ingo@304:         this.domainCrosshairVisible = visible;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Getter method for retrieving the visibility of the domain crosshair.
ingo@767:      *
ingo@767:      * @return Visibility of the domain crosshair.
ingo@767:      */
ingo@304:     public boolean getDomainCrosshairVisible() {
ingo@304:         return domainCrosshairVisible;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Getter method for retrieving the visibility of the range crosshair.
ingo@767:      *
ingo@767:      * @return Visibility of the range crosshair.
ingo@767:      */
ingo@304:     public boolean getRangeCrosshairVisible() {
ingo@304:         return rangeCrosshairVisible;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Setter method for the visibility of the range crosshair.
ingo@767:      *
ingo@767:      * @param visible True, if range crosshair should be visible
ingo@767:      */
ingo@304:     public void setRangeCrosshairVisible(boolean visible) {
ingo@304:         this.rangeCrosshairVisible = visible;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Method to set the bin color of histograms.
ingo@767:      *
ingo@767:      * @param c Bin color
ingo@767:      */
ingo@617:     public void setHistogramBasePaint(Color c) {
ingo@617:         this.histogramBasePaint = c;
ingo@617:     }
ingo@617: 
ingo@617: 
ingo@767:     /**
ingo@767:      * Getter method for retrieving the bin color.
ingo@767:      *
ingo@767:      * @return Bin color
ingo@767:      */
ingo@617:     public Paint getHistogramBasePaint() {
ingo@617:         return histogramBasePaint;
ingo@617:     }
ingo@617: 
ingo@617: 
ingo@767:     /**
ingo@767:      * Take a given xml document and read the configuration of a chart
ingo@767:      * appearance.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     public void applyXMLConfiguration(Document document) {
ingo@304:         log.debug("create XMLChartTheme");
ingo@304: 
ingo@304:         init(document);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Start parsing the different settings from <code>document</code>.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void init(Document document) {
ingo@304:         log.debug("init XMLChartTheme parameters");
ingo@304: 
ingo@304:         initChartParameters(document);
ingo@304:         initTitleParameters(document);
ingo@304:         initSubtitleParameters(document);
ingo@304:         initPlotParameters(document);
ingo@304:         initAxisParameters(document);
ingo@304:         initLegendParameters(document);
ingo@327:         initRenderer(document);
ingo@617:         initHistogramColor(document);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the title of a chart.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void initTitleParameters(Document document) {
ingo@304:         log.debug("init title parameters.");
ingo@304: 
ingo@304:         int     size  = getInt(document, "theme/title/font/size/@value");
ingo@304:         String  type  = getString(document, "theme/title/font/type/@value");
ingo@304:         boolean bold  = getBool(document, "theme/title/font/bold/@value");
ingo@304: 
ingo@304:         setExtraLargeFont(createFont(type, size, bold));
ingo@304: 
ingo@304:         String color = getString(document, "theme/title/font/color/@value");
ingo@304:         Color  c     = decodeColor(color);
ingo@304:         if (c != null)
ingo@304:             setTitlePaint(c);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the subtitle of a chart.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void initSubtitleParameters(Document document) {
ingo@304:         log.debug("init title parameters.");
ingo@304: 
ingo@304:         int     size  = getInt(document, "theme/subtitle/font/size/@value");
ingo@304:         String  type  = getString(document, "theme/subtitle/font/type/@value");
ingo@304:         boolean bold  = getBool(document, "theme/subtitle/font/bold/@value");
ingo@304: 
ingo@304:         setLargeFont(createFont(type, size, bold));
ingo@304: 
ingo@304:         String col  = getString(document, "theme/subtitle/font/color/@value");
ingo@304:         setSubtitlePaint(Color.decode(col));
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the background color of a chart.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void initChartParameters(Document document) {
ingo@304:         log.debug("init chart parameters.");
ingo@304: 
ingo@304:         String bg = getString(document, "theme/chart/background/color/@value");
ingo@304:         Color  c  = decodeColor(bg);
ingo@304:         if (c != null)
ingo@304:             setChartBackgroundPaint(c);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the plot of a chart.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void initPlotParameters(Document document) {
ingo@304:         log.debug("init plot parameters.");
ingo@304: 
ingo@304:         String tmp = null;
ingo@304:         tmp        = getString(document, "theme/plot/background/color/@value");
ingo@304:         Color  c   = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setPlotBackgroundPaint(c);
ingo@304: 
ingo@304:         tmp = getString(document, "theme/plot/outline/color/@value");
ingo@304:         c   = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setPlotOutlinePaint(c);
ingo@304: 
ingo@304:         tmp = getString(document, "theme/plot/domaingridline/color/@value");
ingo@304:         c   = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setDomainGridlinePaint(c);
ingo@304: 
ingo@304:         tmp = getString(document, "theme/plot/rangegridline/color/@value");
ingo@304:         c   = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setRangeGridlinePaint(c);
ingo@304: 
ingo@304:         boolean rangeCrosshairVisible = getBool(
ingo@304:             document, "theme/plot/rangecrosshair/visible/@value");
ingo@304:         setRangeCrosshairVisible(rangeCrosshairVisible);
ingo@304: 
ingo@304:         boolean domainCrosshairVisible = getBool(
ingo@304:             document, "theme/plot/domaincrosshair/visible/@value");
ingo@304:         setDomainCrosshairVisible(domainCrosshairVisible);
ingo@304: 
ingo@304:         int top    = getInt(document, "theme/plot/offset/top/@value");
ingo@304:         int bottom = getInt(document, "theme/plot/offset/bottom/@value");
ingo@304:         int left   = getInt(document, "theme/plot/offset/left/@value");
ingo@304:         int right  = getInt(document, "theme/plot/offset/right/@value");
ingo@304:         setAxisOffset(new RectangleInsets(
ingo@304:             UnitType.RELATIVE,
ingo@304:             top, left, bottom, right)
ingo@304:         );
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the axes of a plot.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void initAxisParameters(Document document) {
ingo@304:         log.debug("init axis parameters.");
ingo@304: 
ingo@304:         String tmp = null;
ingo@304:         tmp        = getString(document, "theme/axis/label/color/@value");
ingo@304:         Color c    = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setAxisLabelPaint(c);
ingo@304: 
ingo@304:         tmp = getString(document, "theme/axis/ticklabel/color/@value");
ingo@304:         c   = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setTickLabelPaint(c);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the legend of a chart.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@304:     private void initLegendParameters(Document document) {
ingo@304:         log.debug("init legend parameters.");
ingo@304: 
ingo@304:         String tmp = null;
ingo@304:         tmp        = getString(document, "theme/legend/font/color/@value");
ingo@304:         Color c    = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setLegendItemPaint(c);
ingo@304: 
ingo@304:         tmp = getString(document, "theme/legend/background/color/@value");
ingo@304:         c   = decodeColor(tmp);
ingo@304:         if (c != null)
ingo@304:             setLegendBackgroundPaint(c);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read parameters configuring the renderer of a plot.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@327:     private void initRenderer(Document document) {
ingo@327:         log.debug("init renderer parameters.");
ingo@327: 
ingo@327:         pointWidth  = getInt(document, "theme/plot/itemrenderer/width/@value");
ingo@327:         log.debug("Read point width of " + pointWidth);
ingo@327:         pointHeight = getInt(document, "theme/plot/itemrenderer/height/@value");
ingo@327:         log.debug("Read point height of " + pointHeight);
ingo@327:         renderLines = getBool(
ingo@327:             document, "theme/plot/itemrenderer/renderLines/@value"
ingo@327:         );
ingo@327:         renderShapes = getBool(
ingo@327:             document, "theme/plot/itemrenderer/renderPoints/@value"
ingo@327:         );
ingo@327:     }
ingo@327: 
ingo@327: 
ingo@767:     /**
ingo@767:      * Read base color of bins in histogram charts.
ingo@767:      *
ingo@767:      * @param document XML document
ingo@767:      */
ingo@617:     private void initHistogramColor(Document document) {
ingo@617:         log.debug("init histogram color");
ingo@617:         String tmp = getString(document, "theme/histogram/bar/color/@value");
ingo@617:         Color c = decodeColor(tmp);
ingo@617: 
ingo@617:         if (c != null)
ingo@617:             setHistogramBasePaint(c);
ingo@617:     }
ingo@617: 
ingo@617: 
ingo@767:     /**
ingo@767:      * Read a xpath expression and return the matched string.
ingo@767:      *
ingo@767:      * @param document Document
ingo@767:      * @param xpath XPath expression
ingo@767:      *
ingo@767:      * @return Matched string
ingo@767:      */
ingo@304:     private static String getString(Document document, String xpath) {
ingo@304:         return Config.getStringXPath(document, xpath);
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read a xpath and turn it into an integer.
ingo@767:      *
ingo@767:      * @param document Document
ingo@767:      * @param xpath XPath expression
ingo@767:      *
ingo@767:      * @return Matched string as integer representation. Return 0 if no integer
ingo@767:      * have been found at <code>xpath</code>.
ingo@767:      */
ingo@304:     private static int getInt(Document document, String xpath) {
ingo@304:         String tmp = getString(document, xpath);
ingo@304: 
ingo@304:         if (tmp != null)
ingo@304:             return Integer.parseInt(tmp);
ingo@304:         else
ingo@304:             return 0;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Read a xpath and turn it into a boolean.
ingo@767:      *
ingo@767:      * @param document Document
ingo@767:      * @param xpath XPath expression
ingo@767:      *
ingo@767:      * @return Matched string as boolean representation. Return false if no
ingo@767:      * boolean have been found at <code>xpath</code>.
ingo@767:      */
ingo@304:     private static boolean getBool(Document document, String xpath) {
ingo@304:         String tmp = getString(document, xpath);
ingo@304: 
ingo@304:         if (tmp != null)
ingo@304:             return Boolean.parseBoolean(tmp);
ingo@304:         else
ingo@304:             return false;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Turns a string into a color using {@link java.awt.Color}.
ingo@767:      *
ingo@788:      * @param color as string
ingo@767:      *
ingo@767:      * @return Color
ingo@767:      */
ingo@304:     protected Color decodeColor(String color) {
ingo@304:         try {
ingo@304:             if (color == null)
ingo@304:                 return null;
ingo@304: 
ingo@304:             return Color.decode(color);
ingo@304:         }
ingo@304:         catch (NumberFormatException nfe) {
ingo@304:             log.warn("Error while parsing color: " + color, nfe);
ingo@304:         }
ingo@304: 
ingo@304:         return null;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Create a font with the given parameters.
ingo@767:      *
ingo@767:      * @param type Font type
ingo@767:      * @param size Font size
ingo@767:      * @param bold Font weight
ingo@767:      *
ingo@767:      * @return Font
ingo@767:      */
ingo@304:     protected Font createFont(String type, int size, boolean bold) {
ingo@304:         Font font = null;
ingo@304:         if (bold)
ingo@304:             font = new Font(type, Font.BOLD, size);
ingo@304:         else
ingo@304:             font = new Font(type, Font.PLAIN, size);
ingo@304: 
ingo@304:         return font;
ingo@304:     }
ingo@304: 
ingo@304: 
ingo@767:     /**
ingo@767:      * Apply settings of this <code>ChartTheme</code> to the given
ingo@767:      * <code>XYPlot</code>.
ingo@767:      *
ingo@767:      * @param plot XYPlot
ingo@767:      */
ingo@788:     @Override
ingo@304:     protected void applyToXYPlot(XYPlot plot) {
ingo@304:         log.debug("apply theme parameter to XYPlot");
ingo@304: 
ingo@304:         super.applyToXYPlot(plot);
ingo@304:         plot.setDomainCrosshairVisible(this.domainCrosshairVisible);
ingo@304:         plot.setRangeCrosshairVisible(this.rangeCrosshairVisible);
ingo@327: 
sascha@778:         AbstractXYItemRenderer renderer = (AbstractXYItemRenderer)
ingo@617:             plot.getRenderer();
ingo@617: 
ingo@617:         if (renderer instanceof XYLineAndShapeRenderer)
ingo@617:             applyToXYLineAndShapeRenderer(plot);
ingo@617: 
ingo@617:         if (renderer instanceof XYBarRenderer)
ingo@617:             applyToXYBarRenderer(plot);
ingo@327:     }
ingo@327: 
ingo@327: 
ingo@767:     /**
ingo@767:      * Apply settings of this <code>ChartTheme</code> to the
ingo@767:      * <code>XYLineAndShapeRenderer</code> of the given <code>XYPlot</code>.
ingo@767:      *
ingo@767:      * @param plot XYPlot
ingo@767:      */
ingo@327:     protected void applyToXYLineAndShapeRenderer(XYPlot plot) {
ingo@327:         if (plot == null)
ingo@327:             return;
ingo@327: 
ingo@327:         XYLineAndShapeRenderer renderer =
ingo@327:             (XYLineAndShapeRenderer) plot.getRenderer();
ingo@327: 
ingo@327:         Ellipse2D.Double point = new Ellipse2D.Double(
ingo@327:             -(pointWidth/2), -(pointHeight/2), pointWidth, pointHeight
ingo@327:         );
ingo@327: 
ingo@327:         renderer.setSeriesShape(0, point);
ingo@327:         renderer.setSeriesShapesVisible(0, renderShapes);
ingo@327:         renderer.setSeriesLinesVisible(0, renderLines);
ingo@327: 
ingo@327:         plot.setRenderer(renderer);
ingo@304:     }
ingo@617: 
ingo@617: 
ingo@767:     /**
ingo@767:      * Apply settings of this <code>ChartTheme</code> to the
ingo@767:      * <code>XYBarRenderer</code> of the given <code>XYPlot</code>.
ingo@767:      *
ingo@767:      * @param plot XYPlot
ingo@767:      */
ingo@617:     protected void applyToXYBarRenderer(XYPlot plot) {
ingo@617:         if (plot == null)
ingo@617:             return;
ingo@617: 
ingo@617:         XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer();
ingo@617: 
ingo@617:         renderer.setSeriesPaint(0, histogramBasePaint);
ingo@617:     }
ingo@304: }
sascha@836: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :