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: * StandardChartTheme. This class takes an xml document with a sascha@778: * bunch of parameters and turns it into a ChartTheme to change ingo@767: * the appearance of charts. ingo@767: * ingo@767: * @author Ingo Weinzierl 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 document. 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 xpath. 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 xpath. 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 ChartTheme to the given ingo@767: * XYPlot. 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 ChartTheme to the ingo@767: * XYLineAndShapeRenderer of the given XYPlot. 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 ChartTheme to the ingo@767: * XYBarRenderer of the given XYPlot. 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 :