zul/src/main/java/org/zkoss/zul/Chart.java
/* Chart.java
Purpose:
Description:
History:
Mon Jul 10 16:57:48 2006, Created by henrichen
Copyright (C) 2006 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zul;
import java.awt.Font;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import org.zkoss.image.AImage;
import org.zkoss.lang.Classes;
import org.zkoss.lang.Library;
import org.zkoss.lang.Objects;
import org.zkoss.lang.Strings;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SerializableEventListener;
import org.zkoss.zk.ui.util.ComponentCloneListener;
import org.zkoss.zul.event.ChartAreaListener;
import org.zkoss.zul.event.ChartDataEvent;
import org.zkoss.zul.event.ChartDataListener;
import org.zkoss.zul.impl.ChartEngine;
/**
* The generic chart component. Developers set proper chart type, data model,
* and the threeD (3D) attribute to draw proper chart.
*
* <p>Chart requires an implementation of an engine ({@link ChartEngine}).
* The default engine is based on JFreeChart and available in ZK PE and ZK EE.
*
* <p>The model and type must
* match to each other; or the result is unpredictable. The 3D chart is not supported
* on all chart type.
*
* <table>
* <tr><th>type</th><th>model</th><th>3D</th></tr>
* <tr><td>area</td><td>{@link CategoryModel} or {@link XYModel}</td><td>No</td></tr>
* <tr><td>bar</td><td>{@link CategoryModel}</td><td>Yes</td></tr>
* <tr><td>bubble</td><td>{@link XYZModel}</td><td>No</td></tr>
* <tr><td>candlestick</td><td>{@link HiLoModel}</td><td>No</td></tr>
* <tr><td>dial</td><td>@{link DialModel}</td><td>No</td></tr>
* <tr><td>gantt</td><td>{@link GanttModel}</td><td>No</td></tr>
* <tr><td>highlow</td><td>{@link HiLoModel}</td><td>No</td></tr>
* <tr><td>histogram</td><td>{@link XYModel}</td><td>No</td></tr>
* <tr><td>line</td><td>{@link CategoryModel} or {@link XYModel}</td><td>Yes</td></tr>
* <tr><td>pie</td><td>{@link PieModel}</td><td>Yes</td></tr>
* <tr><td>polar</td><td>{@link XYModel}</td><td>No</td></tr>
* <tr><td>ring</td><td>{@link PieModel}</td><td>No</td></tr>
* <tr><td>scatter</td><td>{@link XYModel}</td><td>No</td></tr>
* <tr><td>stacked_bar</td><td>{@link CategoryModel}</td><td>Yes</td></tr>
* <tr><td>stacked_area</td><td>{@link CategoryModel} or {@link XYModel}</td><td>No</td></tr>
* <tr><td>step</td><td>{@link XYModel}</td><td>No</td></tr>
* <tr><td>step_area</td><td>{@link XYModel}</td><td>No</td></tr>
* <tr><td>time_series</td><td>{@link XYModel}</td><td>No</td></tr>
* <tr><td>wafermap</td><td>{@link WaferMapModel}</td><td>No</td></tr>
* <tr><td>waterfall</td><td>{@link CategoryModel}</td><td>No</td></tr>
* <tr><td>wind</td><td>{@link XYZModel}</td><td>No</td></tr>
* </table>
*
* @see ChartEngine
* @see ChartModel
* @author henrichen
*/
public class Chart extends Imagemap {
private static final long serialVersionUID = 20091008183601L;
//chart type
public static final String PIE = "pie";
public static final String FUNNEL = "funnel";
public static final String RING = "ring";
public static final String BAR = "bar";
public static final String LINE = "line";
public static final String AREA = "area";
public static final String STACKED_BAR = "stacked_bar";
public static final String COMBINATION = "combination";
public static final String STACKED_AREA = "stacked_area";
public static final String WATERFALL = "waterfall";
public static final String POLAR = "polar";
public static final String SCATTER = "scatter";
public static final String TIME_SERIES = "time_series";
public static final String STEP = "step";
public static final String STEP_AREA = "step_area";
public static final String HISTOGRAM = "histogram";
public static final String CANDLESTICK = "candlestick";
public static final String HIGHLOW = "highlow";
public static final String BUBBLE = "bubble"; //@since 3.5.0
public static final String WAFERMAP = "wafermap"; //@since 3.5.0
public static final String GANTT = "gantt"; //@since 3.5.0
public static final String WIND = "wind"; //@since 3.5.0
public static final String DIAL = "dial"; //@since 3.6.3
private static final Map<String, String> DEFAULT_MODEL = new HashMap<String, String>();
static {
DEFAULT_MODEL.put(PIE, "org.zkoss.zul.SimplePieModel");
DEFAULT_MODEL.put(RING, "org.zkoss.zul.SimplePieModel");
DEFAULT_MODEL.put(BAR, "org.zkoss.zul.SimpleCategoryModel");
DEFAULT_MODEL.put(LINE, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(AREA, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(STACKED_BAR, "org.zkoss.zul.SimpleCategoryModel");
DEFAULT_MODEL.put(STACKED_AREA, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(WATERFALL, "org.zkoss.zul.SimpleCategoryModel");
DEFAULT_MODEL.put(POLAR, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(SCATTER, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(TIME_SERIES, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(STEP, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(STEP_AREA, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(HISTOGRAM, "org.zkoss.zul.SimpleXYModel");
DEFAULT_MODEL.put(CANDLESTICK, "org.zkoss.zul.SimpleHiLoModel");
DEFAULT_MODEL.put(HIGHLOW, "org.zkoss.zul.SimpleHiLoModel");
DEFAULT_MODEL.put(BUBBLE, "org.zkoss.zul.SimpleXYZModel"); //@since 3.5.0
DEFAULT_MODEL.put(WAFERMAP, "org.zkoss.zul.WaferMapModel"); //@since 3.5.0
DEFAULT_MODEL.put(GANTT, "org.zkoss.zul.GanttModel"); //@since 3.5.0
DEFAULT_MODEL.put(WIND, "org.zkoss.zul.SimpleXYZModel"); //@since 3.5.0
DEFAULT_MODEL.put(DIAL, "org.zkoss.zul.DialModel"); //@since 3.6.3
}
//Time Series Chart Period
public static final String YEAR = "year";
public static final String QUARTER = "quarter";
public static final String MONTH = "month";
public static final String WEEK = "week";
public static final String DAY = "day";
public static final String HOUR = "hour";
public static final String MINUTE = "minute";
public static final String SECOND = "second";
public static final String MILLISECOND = "millisecond";
//control variable
private boolean _smartDrawChart; //whether post the smartDraw event already?
private EventListener<Event> _smartDrawChartListener; //the smartDrawListener
private ChartDataListener _dataListener;
private String _type = PIE; //chart type (pie, ring, bar, line, xy, etc)
private boolean _threeD; //whether a 3D chart
//chart related attributes
private String _title; //chart title
private int _intWidth = 400; //default to 400
private int _intHeight = 200; //default to 200
private String _xAxis;
private String _yAxis;
private boolean _showLegend = true; // wether show legend
private boolean _showTooltiptext = true; //wether show tooltiptext
private String _orient = "vertical"; //orient
private ChartAreaListener _areaListener; //callback function when chart area changed
private String _paneColor; // pane's color
private int[] _paneRGB = new int[] { 0xEE, 0xEE, 0xEE }; //pane red, green, blue (0 ~ 255, 0 ~ 255, 0 ~ 255)
private int _paneAlpha = 255; //pane alpha transparency (0 ~ 255, default to 255)
//plot related attributes
private int _fgAlpha = 255; //foreground alpha transparency (0 ~ 255, default to 255)
private String _bgColor;
private int[] _bgRGB = new int[] { 0xFF, 0xFF, 0xFF }; //background red, green, blue (0 ~ 255, 0 ~ 255, 0 ~ 255)
private int _bgAlpha = 255; //background alpha transparency (0 ~ 255, default to 255)
//Time Series Chart related attributes
private TimeZone _tzone;
private String _period;
private String _dateFormat;
//chart data model
private ChartModel _model; //chart data model
//chart engine
private ChartEngine _engine; //chart engine. model and engine is related
//chart Font
private Font _titleFont; //chart's title font
private Font _legendFont; //chart's lengend font
private Font _xAxisTickFont; //chart's x axis tick number font
private Font _xAxisFont; //chart's x axis font
private Font _yAxisTickFont; //chart's y axis tick number font
private Font _yAxisFont; //chart's y axis font
public Chart() {
init();
setWidth("500px");
setHeight("250px");
}
private ChartModel createDefaultModel() {
if (WAFERMAP.equals(getType())) {
return new WaferMapModel(100, 100);
}
final String klass = DEFAULT_MODEL.get(getType());
if (klass != null) {
try {
return (ChartModel) Classes.newInstanceByThread(klass);
} catch (Exception e) {
throw UiException.Aide.wrap(e);
}
} else {
throw new UiException("unknown chart type: " + getType());
}
}
private void init() {
if (_smartDrawChartListener == null) {
_smartDrawChartListener = new SmartDrawListener();
addEventListener("onSmartDrawChart", _smartDrawChartListener);
}
}
private class SmartDrawListener implements SerializableEventListener<Event> {
private static final long serialVersionUID = 20091008183610L;
public void onEvent(Event event) throws Exception {
doSmartDraw();
}
}
private void doSmartDraw() {
if (Strings.isBlank(getType()))
throw new UiException("chart must specify type (pie, bar, line, ...)");
if (_model == null) {
_model = createDefaultModel();
}
if (Strings.isBlank(getWidth()))
throw new UiException("chart must specify width");
if (Strings.isBlank(getHeight()))
throw new UiException("chart must specify height");
try {
final AImage image = new AImage("chart" + new Date().getTime(), getEngine().drawChart(Chart.this));
setContent(image);
} catch (java.io.IOException ex) {
throw UiException.Aide.wrap(ex);
} finally {
_smartDrawChart = false;
}
}
/**
* Set the chart's type (Chart.PIE, Chart.BAR, Chart.LINE, etc.).
*
* <p>Default: pie.
*
*/
public void setType(String type) {
if (Objects.equals(_type, type)) {
return;
}
_type = type;
smartDrawChart();
}
/**
* Get the chart's type.
*/
public String getType() {
return _type;
}
/**
* Set true to show three dimensional graph (If a type of chart got no 3d peer, this is ignored).
*/
public void setThreeD(boolean b) {
if (_threeD == b) {
return;
}
_threeD = b;
smartDrawChart();
}
/**
* Whether a 3d chart.
*/
public boolean isThreeD() {
return _threeD;
}
/**
* Set the chart's title.
* @param title the chart's title.
*
*/
public void setTitle(String title) {
if (Objects.equals(_title, title)) {
return;
}
_title = title;
smartDrawChart();
}
/**
* Get the chart's title.
*/
public String getTitle() {
return _title;
}
/**
* Override super class to prepare the int width.
*/
public void setWidth(String w) {
if (Objects.equals(w, getWidth())) {
return;
}
_intWidth = stringToInt(w);
super.setWidth0(w); //ZK-2895: call the method which do not check vflex
smartDrawChart();
}
/** Overrides the method in HtmlBasedComponent, not to check using hflex and width at the same time
* @since 8.0.1
*/
@Override
public void setHflex(String flex) {
super.setHflex0(flex);
}
/**
* Get the chart int width in pixel; to be used by the derived subclass.
*/
public int getIntWidth() {
return _intWidth;
}
/**
* Override super class to prepare the int height.
*/
public void setHeight(String h) {
if (Objects.equals(h, getHeight())) {
return;
}
_intHeight = stringToInt(h);
super.setHeight0(h); //ZK-2895: call the method which do not check hflex
smartDrawChart();
}
/** Overrides the method in HtmlBasedComponent, not to check using vflex and height at the same time
* @since 8.0.1
*/
@Override
public void setVflex(String flex) {
super.setVflex0(flex);
}
/**
* Get the chart int width in pixel; to be used by the derived subclass.
*/
public int getIntHeight() {
return _intHeight;
}
/**
* Set the label in xAxis.
* @param label label in xAxis.
*/
public void setXAxis(String label) {
if (Objects.equals(_xAxis, label)) {
return;
}
_xAxis = label;
smartDrawChart();
}
/**
* Get the label in xAxis.
*/
public String getXAxis() {
return _xAxis;
}
/**
* Set the label in yAxis.
* @param label label in yAxis.
*/
public void setYAxis(String label) {
if (Objects.equals(_yAxis, label)) {
return;
}
_yAxis = label;
smartDrawChart();
}
/**
* Get the label in yAxis.
*/
public String getYAxis() {
return _yAxis;
}
/**
* whether show the chart's legend.
* @param showLegend true if want to show the legend (default to true).
*/
public void setShowLegend(boolean showLegend) {
if (_showLegend == showLegend) {
return;
}
_showLegend = showLegend;
smartDrawChart();
}
/**
* Check whether show the legend of the chart.
*/
public boolean isShowLegend() {
return _showLegend;
}
/**
* whether show the chart's tooltip.
* @param showTooltiptext true if want to pop the tooltiptext (default to true).
*/
public void setShowTooltiptext(boolean showTooltiptext) {
if (_showTooltiptext == showTooltiptext) {
return;
}
_showTooltiptext = showTooltiptext;
smartDrawChart();
}
/**
* Check whether show the tooltiptext.
*/
public boolean isShowTooltiptext() {
return _showTooltiptext;
}
/**
* Set the pane alpha (transparency, 0 ~ 255).
* @param alpha the transparency of pane color (0 ~ 255, default to 255 opaque).
*/
public void setPaneAlpha(int alpha) {
if (alpha == _paneAlpha) {
return;
}
if (alpha > 255 || alpha < 0) {
alpha = 255;
}
_paneAlpha = alpha;
smartDrawChart();
}
/**
* Get the pane alpha (transparency, 0 ~ 255, opacue).
*/
public int getPaneAlpha() {
return _paneAlpha;
}
/**
* Set the pane color of the chart.
* @param color in #RRGGBB format (hexadecimal).
*/
public void setPaneColor(String color) {
if (Objects.equals(color, _paneColor)) {
return;
}
_paneColor = color;
if (_paneColor == null) {
_paneRGB = null;
} else {
_paneRGB = new int[3];
decode(_paneColor, _paneRGB);
}
smartDrawChart();
}
/**
* Get the pane color of the chart (in string as #RRGGBB).
* null means default.
*/
public String getPaneColor() {
return _paneColor;
}
/**
* Get the pane color in int array (0: red, 1: green, 2:blue).
* null means default.
*/
public int[] getPaneRGB() {
return _paneRGB;
}
/**
* Set the foreground alpha (transparency, 0 ~ 255).
* @param alpha the transparency of foreground color (0 ~ 255, default to 255 opaque).
*/
public void setFgAlpha(int alpha) {
if (alpha == _fgAlpha) {
return;
}
if (alpha > 255 || alpha < 0) {
alpha = 255;
}
_fgAlpha = alpha;
smartDrawChart();
}
/**
* Get the foreground alpha (transparency, 0 ~ 255, opaque).
*/
public int getFgAlpha() {
return _fgAlpha;
}
/**
* Set the background alpha (transparency, 0 ~ 255).
* @param alpha the transparency of background color (0 ~ 255, default to 255 opaque).
*/
public void setBgAlpha(int alpha) {
if (alpha == _bgAlpha) {
return;
}
if (alpha > 255 || alpha < 0) {
alpha = 255;
}
_bgAlpha = alpha;
smartDrawChart();
}
/**
* Get the background alpha (transparency, 0 ~ 255, opaque).
*/
public int getBgAlpha() {
return _bgAlpha;
}
/**
* Set the background color of the chart.
* @param color in #RRGGBB format (hexadecimal).
*/
public void setBgColor(String color) {
if (Objects.equals(color, _bgColor)) {
return;
}
_bgColor = color;
if (_bgColor == null) {
_bgRGB = null;
} else {
_bgRGB = new int[3];
decode(_bgColor, _bgRGB);
}
smartDrawChart();
}
/**
* Get the background color of the chart (in string as #RRGGBB).
* null means default.
*/
public String getBgColor() {
return _bgColor;
}
/**
* Get the background color in int array (0: red, 1: green, 2:blue).
* null means default.
*/
public int[] getBgRGB() {
return _bgRGB;
}
/**
* Set the chart orientation.
* @param orient vertical or horizontal (default to vertical)
*/
public void setOrient(String orient) {
if (Objects.equals(orient, _orient)) {
return;
}
_orient = orient;
smartDrawChart();
}
/**
* Get the chart orientation (vertical or horizontal)
*/
public String getOrient() {
return _orient;
}
/** Returns the time zone that this Time Series Chart belongs to, or null if
* the default time zone is used.
* <p>The default time zone is determined by {@link org.zkoss.util.TimeZones#getCurrent}.
*/
public TimeZone getTimeZone() {
return _tzone;
}
/** Sets the time zone that this Time Series Chart belongs to, or null if
* the default time zone is used.
* <p>The default time zone is determined by {@link org.zkoss.util.TimeZones#getCurrent}.
*/
public void setTimeZone(TimeZone tzone) {
if (Objects.equals(tzone, _tzone)) {
return;
}
_tzone = tzone;
smartDrawChart();
}
/** Returns the period used in Time Series Chart. The value can be
* "millisecond", "second", "minute", "hour", "day", "week", "month", "quarter", and "year".
* default is "millisecond" if not specified.
*/
public String getPeriod() {
return _period;
}
/** Sets the period used in Time Series Chart. The value can be
* "millisecond", "second", "minute", "hour", "day", "week", "month", "quarter", and "year".
*/
public void setPeriod(String period) {
if (Objects.equals(period, _period)) {
return;
}
_period = period;
smartDrawChart();
}
/**
* Returns the date format used by date related Chart.
* @return the date format used by date related Chart..
*/
public String getDateFormat() {
return _dateFormat;
}
/**
* Sets the date format used by date related Chart.
* @param format
*/
public void setDateFormat(String format) {
if (Objects.equals(format, _dateFormat)) {
return;
}
_dateFormat = format;
smartDrawChart();
}
/** Returns the chart model associated with this chart, or null
* if this chart is not associated with any chart data model.
*/
public ChartModel getModel() {
return _model;
}
/** Sets the chart model associated with this chart.
* If a non-null model is assigned, no matter whether it is the same as
* the previous, it will always cause re-render.
*
* @param model the chart model to associate, or null to dissociate
* any previous model.
* @exception UiException if failed to initialize with the model
*/
public void setModel(ChartModel model) {
if (_model != model) {
if (_model != null) {
_model.removeChartDataListener(_dataListener);
}
_model = model;
initDataListener();
}
//Always redraw
smartDrawChart();
}
/** Sets the model by use of a class name.
* It creates an instance automatically.
*/
public void setModel(String clsnm) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
InstantiationException, java.lang.reflect.InvocationTargetException {
if (clsnm != null) {
setModel((ChartModel) Classes.newInstanceByThread(clsnm));
}
}
/**
* Returns the title font of this chart. If you saw squares rather than correct
* words in title, check whether the default title font supports your
* characters (e.g. Chinese). You probably have to set this font accordingly.
* @return the title font
*/
public Font getTitleFont() {
return _titleFont;
}
/**
* Sets the title font of this chart. If you saw squares rather than correct
* words in title, check whether the default title font supports your
* characters (e.g. Chinese). You probably have to set this font accordingly.
* @param font the title font of this chart
*/
public void setTitleFont(Font font) {
if (Objects.equals(font, _titleFont)) {
return;
}
_titleFont = font;
smartDrawChart();
}
/**
* Returns the legend font of this chart. If you saw squares rather than correct
* words in legend, check whether the default legend font supports your
* characters (e.g. Chinese). You probably have to set this font accordingly.
* @return the title font
*/
public Font getLegendFont() {
return _legendFont;
}
/**
* Sets the legend font of this chart. If you saw squares rather than correct
* words in legend, check whether the default legend font supports your
* characters (e.g. Chinese). You probably have to set this font accordingly.
* @param font the legend font of this chart
*/
public void setLegendFont(Font font) {
if (Objects.equals(_legendFont, font)) {
return;
}
_legendFont = font;
smartDrawChart();
}
/**
* Returns the tick number font of x axis of this chart. If you saw squares
* rather than correct words in x axis tick, check whether the default x axis
* tick font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @return the tick number font of x axis of this chart
*/
public Font getXAxisTickFont() {
return _xAxisTickFont;
}
/**
* Sets the tick number font of x axis of this chart. If you saw squares
* rather than correct words in x axis tick, check whether the default x axis
* tick font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @param axisTickFont the tick number font of x axis of this chart
*/
public void setXAxisTickFont(Font axisTickFont) {
if (Objects.equals(_xAxisTickFont, axisTickFont)) {
return;
}
_xAxisTickFont = axisTickFont;
smartDrawChart();
}
/**
* Returns the label font of x axis of this chart. If you saw squares
* rather than correct words in x axis label, check whether the default x axis
* label font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @return the label font of x axis of this chart
*/
public Font getXAxisFont() {
return _xAxisFont;
}
/**
* Sets the label font of x axis of this chart. If you saw squares
* rather than correct words in x axis label, check whether the default x axis
* label font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @param axisFont the label font of x axis of this chart
*/
public void setXAxisFont(Font axisFont) {
if (Objects.equals(_xAxisFont, axisFont)) {
return;
}
_xAxisFont = axisFont;
smartDrawChart();
}
/**
* Returns the tick number font of y axis of this chart. If you saw squares
* rather than correct words in y axis tick, check whether the default y axis
* tick font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @return the tick number font of y axis of this chart
*/
public Font getYAxisTickFont() {
return _yAxisTickFont;
}
/**
* Sets the tick number font of y axis of this chart. If you saw squares
* rather than correct words in y axis tick, check whether the default y axis
* tick font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @param axisTickFont the tick number font of y axis of this chart
*/
public void setYAxisTickFont(Font axisTickFont) {
if (Objects.equals(_yAxisTickFont, axisTickFont)) {
return;
}
_yAxisTickFont = axisTickFont;
smartDrawChart();
}
/**
* Returns the label font of y axis of this chart. If you saw squares
* rather than correct words in y axis label, check whether the default y axis
* label font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @return the label font of y axis of this chart
*/
public Font getYAxisFont() {
return _yAxisFont;
}
/**
* Sets the label font of y axis of this chart. If you saw squares
* rather than correct words in y axis label, check whether the default y axis
* label font supports your characters (e.g. Chinese). You probably
* have to set this font accordingly.
* @param axisFont the tick number font of y axis of this chart
*/
public void setYAxisFont(Font axisFont) {
if (Objects.equals(_yAxisFont, axisFont)) {
return;
}
_yAxisFont = axisFont;
smartDrawChart();
}
/** Returns the implementation chart engine.
* @exception UiException if failed to load the engine.
*/
public ChartEngine getEngine() throws UiException {
if (_engine == null)
_engine = newChartEngine();
return _engine;
}
/** Instantiates the default chart engine.
* It is called, if {@link #setEngine} is not called with non-null
* engine.
*
* <p>By default, it looks up the library property called
* org.zkoss.zul.chart.engine.class.
* If found, the value is assumed to be
* the class name of the chart engine (it must implement
* {@link ChartEngine}).
* If not found, {@link UiException} is thrown.
*
* <p>Derived class might override this method to provide your
* own default class.
*
* @exception UiException if failed to instantiate the engine
* @since 3.0.0
*/
protected ChartEngine newChartEngine() throws UiException {
final String PROP = "org.zkoss.zul.chart.engine.class";
final String klass = Library.getProperty(PROP);
if (klass == null)
throw new UiException("Library property, " + PROP + ", required");
final Object v;
try {
v = Classes.newInstanceByThread(klass);
} catch (Exception ex) {
throw UiException.Aide.wrap(ex);
}
if (!(v instanceof ChartEngine))
throw new UiException(ChartEngine.class + " must be implemented by " + v);
return (ChartEngine) v;
}
/** Sets the chart engine.
*/
public void setEngine(ChartEngine engine) {
if (_engine != engine) {
_engine = engine;
}
//Always redraw
smartDrawChart();
}
/** Sets the chart engine by use of a class name.
* It creates an instance automatically.
*/
public void setEngine(String clsnm) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
InstantiationException, java.lang.reflect.InvocationTargetException {
if (clsnm != null) {
setEngine((ChartEngine) Classes.newInstanceByThread(clsnm));
}
}
private void initDataListener() {
if (_dataListener == null) {
_dataListener = new MyChartDataListener();
_model.addChartDataListener(_dataListener);
}
}
private class MyChartDataListener implements ChartDataListener, Serializable {
private static final long serialVersionUID = 20091008183622L;
public void onChange(ChartDataEvent event) {
smartDrawChart();
}
}
/** Returns the renderer to render each area, or null if the default
* renderer is used.
*/
public ChartAreaListener getAreaListener() {
return _areaListener;
}
/** Sets the renderer which is used to render each area.
*
* <p>Note: changing a render will not cause the chart to re-render.
* If you want it to re-render, you could call smartDraw.
*
* @param listener the area listener, or null to ignore it.
* @exception UiException if failed to initialize.
*/
public void setAreaListener(ChartAreaListener listener) {
if (_areaListener != listener) {
_areaListener = listener;
}
}
/** Sets the renderer by use of a class name.
* It creates an instance automatically.
*/
public void setAreaListener(String clsnm) throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InstantiationException, java.lang.reflect.InvocationTargetException {
if (clsnm != null) {
setAreaListener((ChartAreaListener) Classes.newInstanceByThread(clsnm));
}
}
/**
* mark a draw flag to inform that this Chart needs update.
*/
protected void smartDrawChart() {
if (_smartDrawChart) { //already mark smart draw
return;
}
_smartDrawChart = true;
Events.postEvent("onSmartDrawChart", this, null);
}
//-- utilities --//
/*package*/ static void decode(String color, int[] rgb) {
if (color == null) {
return;
}
if (color.length() != 7 || color.charAt(0) != '#') {
throw new UiException("Incorrect color format (#RRGGBB) : " + color);
}
rgb[0] = Integer.parseInt(color.substring(1, 3), 16);
rgb[1] = Integer.parseInt(color.substring(3, 5), 16);
rgb[2] = Integer.parseInt(color.substring(5, 7), 16);
}
/*package*/ static int stringToInt(String str) {
int j = str.lastIndexOf("px");
if (j > 0) {
final String num = str.substring(0, j);
return Integer.parseInt(num);
}
j = str.lastIndexOf("pt");
if (j > 0) {
final String num = str.substring(0, j);
return (int) (Integer.parseInt(num) * 1.3333);
}
j = str.lastIndexOf("em");
if (j > 0) {
final String num = str.substring(0, j);
return (int) (Integer.parseInt(num) * 13.3333);
}
return Integer.parseInt(str);
}
public boolean addEventListener(String evtnm, EventListener<? extends Event> listener) {
final boolean ret = super.addEventListener(evtnm, listener);
if (Events.ON_CLICK.equals(evtnm) && ret)
smartDrawChart(); //since Area has to generate
return ret;
}
//Cloneable//
public Object clone() {
final Chart clone = (Chart) super.clone();
// Due to the not unique ID of the area component creating in JFreeChartEngine, we have to clear
// all its children first.
clone.getChildren().clear();
clone._smartDrawChartListener = null;
clone._smartDrawChart = false;
clone.init();
clone.doSmartDraw();
if (clone._model != null) {
if (clone._model instanceof ComponentCloneListener) {
final ChartModel model = (ChartModel) ((ComponentCloneListener) clone._model).willClone(clone);
if (model != null)
clone._model = model;
}
clone._dataListener = null;
clone.initDataListener();
}
return clone;
}
public void onPageAttached(Page newpage, Page oldpage) {
super.onPageAttached(newpage, oldpage);
if (_model != null) {
smartDrawChart();
if (_dataListener != null) {
_model.removeChartDataListener(_dataListener);
_model.addChartDataListener(_dataListener);
}
}
}
public void onPageDetached(Page page) {
super.onPageDetached(page);
if (_model != null && _dataListener != null)
_model.removeChartDataListener(_dataListener);
}
}