package er.plot;
import java.lang.reflect.Method;
import java.util.Enumeration;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.Dataset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webobjects.appserver.WOContext;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSForwardException;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
/**
* Display a category chart - a chart where you have 2 or 3 dimensions. The most important binding is <code>items</code> which
* should contain an array of objects from which the values <code>xNameKey</code>, <code>yNameKey</code>, <code>categoryKey</code> and <code>
* valueKey</code> are retrieved. For example, you might have an array of line items,
* with a valueKey <code>amount</code>, an <code>xNameKey</code> with <code>invoice.datePurchased</code>, <code>yNameKey</code>, <code>categoryKey</code>and a nameKey <code>product.name</code>.
*
* @binding name the name of the chart
* @binding chartType the type of the chart (possible values depend on the concrete subclass)
* @binding imageType the type of the image to show: <code>png</code> (default) or <code>jpeg</code>
* @binding width the width of the chart (400 pixel if not specified)
* @binding height the height of the chart (400 pixel if not specified)
* @binding dataset Dataset to use. If this is given, then items, nameKey, valueKey and categoryKey are not considered.
* @binding items array of values to display the chart for
* @binding nameKey the key for the name (must return Comparable)
* @binding valueKey the key for the value (must return Number)
* @binding categoryKey the key for the categories (optional, must return Comparable)
* @binding xName the name for the x axis (String)
* @binding yName the name for the y axis (String)
* @binding showLegends true, if legends should be shown
* @binding showToolTips true, if tool tips should be shown
* @binding showUrls true, if urls should be shown
* @binding orientation either "horizontal" (default) or "vertical"
* @binding chart Chart to use instead of the created one. If this binding is set-able, then it will be set to the actually used chart
* @binding configuration NSDictionary that will be applied to the chart via key-value-coding prior to rendering. Contains
* entries like <code>antiAlias=true</code> or <code>categoryPlot.dataAreaRatio = 0.8</code>.
* @author ak
*/
public class ERPCategoryChart extends ERPChart {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(ERPCategoryChart.class);
public static final NSArray<String> SUPPORTED_TYPES = new NSArray<>(new String[]{
"BarChart", "StackedBarChart", "BarChart3D", "StackedBarChart3D", "AreaChart",
"StackedAreaChart", "LineChart", "WaterfallChart"
});
protected String _categoryKey;
protected String _yName;
protected String _xName;
protected PlotOrientation _orientation;
public ERPCategoryChart(WOContext context) {
super(context);
}
@Override
public void reset() {
super.reset();
_xName = null;
_yName = null;
_categoryKey = null;
_orientation = null;
}
public String categoryKey() {
if(_categoryKey == null) {
_categoryKey = stringValueForBinding("categoryKey", null);
}
return _categoryKey;
}
public String xName() {
if(_xName == null) {
_xName = stringValueForBinding("xName", "xName");
}
return _xName;
}
public String yName() {
if(_yName == null) {
_yName = stringValueForBinding("yName", "yName");
}
return _yName;
}
public PlotOrientation orientation() {
if(_orientation == null) {
_orientation = ("horizontal".equals(stringValueForBinding("orientation", "vertical")) ?
PlotOrientation.HORIZONTAL : PlotOrientation.VERTICAL);
}
return _orientation;
}
@Override
protected NSArray<String> supportedTypes() {
return SUPPORTED_TYPES;
}
@Override
protected JFreeChart createChart() {
JFreeChart chart = null;
String name = stringValueForBinding("name", "");
Class<ChartFactory> clazz = ChartFactory.class;
try {
Method method = clazz.getDeclaredMethod("create" + chartType(), new Class[] {
String.class, String.class, String.class, CategoryDataset.class, PlotOrientation.class,
boolean.class, boolean.class, boolean.class
});
chart = (JFreeChart) method.invoke(clazz, new Object[] {name, xName(), yName(), dataset(), orientation(),
(showLegends() ? Boolean.TRUE : Boolean.FALSE),
(showToolTips() ? Boolean.TRUE : Boolean.FALSE),
(showUrls() ? Boolean.TRUE : Boolean.FALSE )
});
} catch(Throwable t) {
log.error("Could not create chart.", t);
throw NSForwardException._runtimeExceptionForThrowable(t);
}
return chart;
}
@Override
protected Dataset createDataset() {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for(Enumeration<?> items = items().objectEnumerator(); items.hasMoreElements(); ) {
Object item = items.nextElement();
Comparable<?> name = (Comparable<?>)NSKeyValueCodingAdditions.Utility.valueForKeyPath(item, nameKey());
Number value = (Number)NSKeyValueCodingAdditions.Utility.valueForKeyPath(item, valueKey());
Comparable<?> category = null;
if(categoryKey() != null) {
category = (Comparable<?>)NSKeyValueCodingAdditions.Utility.valueForKeyPath(item, categoryKey());
}
dataset.setValue(value, name, category);
}
return dataset;
}
}