package er.plot;
import java.io.ByteArrayOutputStream;
import java.util.Enumeration;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.imagemap.ImageMapUtilities;
import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator;
import org.jfree.chart.imagemap.URLTagFragmentGenerator;
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.NSData;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSForwardException;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
import er.extensions.components.ERXStatelessComponent;
import er.extensions.foundation.ERXAssert;
/**
* Abstract superclass of the charts. The most important binding is <code>items</code> which
* should contain an array of objects from which the values <code>nameKey</code> and <code>
* valueKey</code> are retrieved. For example, you might have an array of line items,
* with a valueKey <code>amount</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 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 chart Chart to use instead of the created one. If this binding is setable, 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 abstract class ERPChart extends ERXStatelessComponent {
/**
* 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(ERPChart.class);
protected static final int DEFAULT_SIZE = 400;
protected NSData _imageData;
public String _imageKey;
public String _imageMap;
public String _imageMapName;
protected NSArray<?> _items;
protected String _name;
protected String _chartType;
protected String _imageType;
protected String _nameKey;
protected String _valueKey;
protected int _width = 0;
protected int _height = 0;
protected Dataset _dataset;
protected JFreeChart _chart;
protected NSDictionary<String,?> _configuration;
/**
* Public constructor
* @param context the context
*/
public ERPChart(WOContext context) {
super(context);
}
public NSArray<?> items() {
if(_items == null) {
_items = (NSArray<?>)valueForBinding("items");
ERXAssert.DURING.notNull("items", _items);
}
return _items;
}
public String nameKey() {
if(_nameKey == null) {
_nameKey = (String)valueForBinding("nameKey");
ERXAssert.DURING.notNull("nameKey", _nameKey);
}
return _nameKey;
}
public String valueKey() {
if(_valueKey == null) {
_valueKey = (String)valueForBinding("valueKey");
ERXAssert.DURING.notNull("valueKey", _valueKey);
}
return _valueKey;
}
public int width() {
if(_width == 0) {
_width = intValueForBinding("width", DEFAULT_SIZE);
}
return _width;
}
public int height() {
if(_height == 0) {
_height = intValueForBinding("height", DEFAULT_SIZE);
}
return _height;
}
public String chartType() {
if(_chartType == null) {
_chartType = (String)valueForBinding("chartType");
ERXAssert.DURING.notNull("chartType", _chartType);
ERXAssert.DURING.isTrue("chartType "+_chartType +" is not in supported types: " + supportedTypes(),
supportedTypes().containsObject(_chartType));
}
return _chartType;
}
public String imageType() {
if(_imageType == null) {
_imageType = stringValueForBinding("imageType", "image/png");
}
return _imageType;
}
public boolean showLegends() {
return booleanValueForBinding("showLegends", true);
}
public boolean showUrls() {
return booleanValueForBinding("showUrls", false);
}
public boolean showToolTips() {
return booleanValueForBinding("showToolTips", true);
}
@SuppressWarnings("unchecked")
public NSDictionary<String,?> configuration() {
if(_configuration == null) {
_configuration = (NSDictionary<String,Object>)valueForBinding("configuration");
if(_configuration == null) {
_configuration = NSDictionary.EmptyDictionary;
}
}
return _configuration;
}
@Override
public void reset() {
super.reset();
_imageData = null;
_imageKey = null;
_imageType = null;
_items = null;
_name = null;
_chartType = null;
_nameKey = null;
_valueKey = null;
_width = 0;
_height = 0;
_dataset = null;
_chart = null;
_configuration = null;
}
protected abstract JFreeChart createChart();
protected abstract Dataset createDataset();
protected abstract NSArray<String> supportedTypes();
public Dataset dataset() {
if(_dataset == null) {
if(hasBinding("dataset") && canGetValueForBinding("dataset")) {
_dataset = (Dataset)valueForBinding("dataset");
}
if(_dataset == null) {
_dataset = createDataset();
if(canSetValueForBinding("dataset")) {
setValueForBinding(_dataset, "dataset");
}
}
}
return _dataset;
}
public JFreeChart chart() {
if(_chart == null) {
if(hasBinding("chart") && canGetValueForBinding("chart")) {
_chart = (JFreeChart)valueForBinding("chart");
}
if(_chart == null) {
_chart = createChart();
if(hasBinding("chart") && canSetValueForBinding("chart")) {
setValueForBinding(_chart, "chart");
}
for (Enumeration<String> keys = configuration().keyEnumerator(); keys.hasMoreElements();) {
String keypath = keys.nextElement();
Object value = configuration().objectForKey(keypath);
NSKeyValueCodingAdditions.Utility.takeValueForKeyPath(_chart, value, keypath);
}
}
}
return _chart;
}
public NSData imageData() {
if(_imageData == null) {
try {
JFreeChart chart = chart();
if(chart != null) {
ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
ChartRenderingInfo info = null;
if(showToolTips() || showUrls()) {
info = new ChartRenderingInfo(new StandardEntityCollection());
}
if("image/jpeg".equals(imageType())) {
ChartUtilities.writeChartAsJPEG(imageStream, chart, width(), height(), info);
} else {
ChartUtilities.writeChartAsPNG(imageStream, chart, width(), height(), info, true, 0);
}
if(showToolTips() || showUrls()) {
_imageMapName = "ERP" + System.identityHashCode(chart);
ToolTipTagFragmentGenerator toolTipGenerator = null;
URLTagFragmentGenerator urlTagFragmentGenerator = null;
if(showToolTips()) {
toolTipGenerator = new ToolTipTagFragmentGenerator() {
public String generateToolTipFragment(String toolTip) {
return " title=\"" +toolTip + "\"";
}
};
}
if(showUrls()) {
urlTagFragmentGenerator = new URLTagFragmentGenerator() {
public String generateURLFragment(String url) {
return " href=\""+url+"\"";
}
};
}
// for jfreechart-1.0.x use this line
_imageMap = ImageMapUtilities.getImageMap(_imageMapName, info, toolTipGenerator, urlTagFragmentGenerator);
// for jfreechart-0.9.x use this line
// _imageMap = ImageMapUtil.getImageMap(_imageMapName, info, toolTipGenerator, urlTagFragmentGenerator);
}
_imageData = new NSData(imageStream.toByteArray());
}
} catch (Exception ex) {
log.error("Could not convert chart to NSData.", ex);
NSForwardException._runtimeExceptionForThrowable(ex);
}
}
return _imageData;
}
public String imageMap() {
imageData();
return _imageMap;
}
public String otherTagString() {
String map = imageMap();
return map == null ? null : "usemap=\"#" + _imageMapName + "\"";
}
}