package binky.reportrunner.ui.actions.dashboard;
import java.awt.Color;
import java.awt.Font;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.BarRenderer3D;
import org.jfree.chart.renderer.category.MinMaxCategoryRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.util.Rotation;
import binky.reportrunner.data.DashboardData;
import binky.reportrunner.data.RunnerDashboardChart;
import binky.reportrunner.data.RunnerDashboardItem;
import binky.reportrunner.data.RunnerDashboardItem.ItemType;
import binky.reportrunner.data.RunnerDashboardSampler;
import binky.reportrunner.data.RunnerDashboardChart.Orientation;
import binky.reportrunner.data.sampling.SamplingData;
import binky.reportrunner.data.sampling.TrendData;
import binky.reportrunner.exceptions.ChartDefinitionException;
import binky.reportrunner.ui.actions.dashboard.base.BaseDashboardAction;
import binky.reportrunner.ui.util.TextToImage;
import binky.reportrunner.ui.util.TextToImage.ImageFileFormat;
public class GetChart extends BaseDashboardAction {
private static final int LARGE_X = 925;
private static final int LARGE_Y = 895;
private static final int MID_X = 714;
private static final int MID_Y = 594;
private static final int SMALL_X = 353;
private static final int SMALL_Y = 295;
private static final long serialVersionUID = 1L;
private static String ERROR_HEADER="An error occured (see logs for extended detail):";
private InputStream inputStream;
private Integer itemId;
private static final Logger logger = Logger.getLogger(GetChart.class);
@Override
public String execute() throws Exception {
RunnerDashboardItem item = super.getDashboardService().getItem(itemId);
// noddy security check
if (!doesUserHaveGroup(item.getGroup().getGroupName())) {
throw new SecurityException(
"user does not have permission to group: " + groupName);
}
int width = LARGE_X;
int height= LARGE_Y;
switch (item.getWidth()) {
case Large:
width = LARGE_X;
break;
case Medium:
width = MID_X;
break;
case Small:
width = SMALL_X;
}
switch (item.getHeight()) {
case Large:
height = LARGE_Y;
break;
case Medium:
height = MID_Y;
break;
case Small:
height = SMALL_Y;
}
boolean ok=false;
String errorMessage="";
byte[] imageData=new byte[0];
try {
JFreeChart chart;
if (item.getItemType()==ItemType.Sampler) {
chart = getSamplerChart((RunnerDashboardSampler)item);
} else {
chart = getChart((RunnerDashboardChart)item);
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
ChartUtilities.writeChartAsPNG(os, chart, width, height);
os.close();
imageData=os.toByteArray();
ok=true;
} catch(Throwable t) {
logger.warn("error producing chart: " + t.getMessage(),t);
errorMessage=t.getMessage();
}
if (!ok) {
TextToImage t2i = new TextToImage();
imageData=t2i.getImageForText(new String[]{ERROR_HEADER,errorMessage}, Color.RED, Color.WHITE, new Font("Serif", Font.PLAIN, 12), width, height, 10, 15, ImageFileFormat.PNG);
}
this.inputStream=new ByteArrayInputStream(imageData);
return SUCCESS;
}
private JFreeChart getChart(RunnerDashboardChart item) throws Exception {
//sanity check the item
if (item.getXaxisColumn()==null||item.getXaxisColumn().isEmpty()) throw new ChartDefinitionException("X-Axis Column not defined");
if (item.getValueColumn()==null||item.getXaxisColumn().isEmpty()) throw new ChartDefinitionException("Value Column not defined");
List<Map<String, Object>> values = new LinkedList<Map<String, Object>>();
// populate the values
if (item.getData() != null && item.getData().size() > 0) {
int currentRowNumber = 0;
Map<String, Object> currentRow = new HashMap<String, Object>();
for (DashboardData d : item.getData()) {
if (d.getRowNumber() > currentRowNumber) {
if (currentRowNumber > 0) {
if (logger.isTraceEnabled()) {
for (String s : currentRow.keySet()) {
logger.trace("current row " + currentRowNumber
+ " has column " + s
+ " with value of " + currentRow.get(s)
+ " key has hash value of "
+ s.hashCode());
}
}
values.add(currentRow);
currentRow = new HashMap<String, Object>();
}
currentRowNumber = d.getRowNumber();
}
Object value;
switch (d.getDataType()) {
case Types.DECIMAL:
case Types.FLOAT:
case Types.REAL:
case Types.DOUBLE:
case Types.BIGINT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
value = new BigDecimal(d.getValue());
break;
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
value = new Date(Long.parseLong(d.getValue()));
break;
default:
value = d.getValue();
}
currentRow.put(d.getColumnName(), value);
}
} else {
throw new ChartDefinitionException("Empty dataset for item: " + itemId);
}
List<String> xLabels = new LinkedList<String>();
for (Map<String, Object> row : values) {
logger.trace("fetching value for column: " + item.getXaxisColumn()
+ " hashcode of " + item.getXaxisColumn().hashCode());
if (logger.isTraceEnabled()) {
for (String s : row.keySet()) {
logger.trace("current row has column " + s
+ " with value of " + row.get(s)
+ " key has hash value of " + s.hashCode());
}
}
String label = row.get(item.getXaxisColumn()).toString();
if (!xLabels.contains(label)) {
xLabels.add(label);
}
}
if (xLabels.size() == 0) {
Exception e = new Exception("invalid labels column identifier "
+ item.getXaxisColumn() + " for item " + item.getItemId());
logger.error(e.getMessage(), e);
throw e;
}
List<Object> series = new LinkedList<Object>();
for (Map<String, Object> row : values) {
Object seriesName;
if ((item.getSeriesNameColumn() == null)
|| (item.getSeriesNameColumn().isEmpty())) {
seriesName = "VALUE";
} else {
seriesName = (String) row.get(item.getSeriesNameColumn());
logger.trace("found series" + seriesName);
}
if (!series.contains(seriesName)) {
series.add(seriesName);
}
}
if (series.size() == 0) {
Exception e = new Exception("invalid series column identifier "
+ item.getSeriesNameColumn() + " for item id "
+ item.getItemId());
logger.error(e.getMessage(), e);
throw e;
}
JFreeChart chart;
switch (item.getChartType()) {
case PIE:
// need to figure what to do about multiple series
if (series != null && series.size() > 0) {
Object s = series.get(0);
DefaultPieDataset result = new DefaultPieDataset();
for (Map<String, Object> row : values) {
if (StringUtils.isBlank(item.getSeriesNameColumn())) {
Number yValue = (Number) row.get(item.getValueColumn());
Object xValue = row.get(item.getXaxisColumn());
result.setValue(xValue.toString(), yValue);
} else {
Object sValue = row.get(item.getSeriesNameColumn());
Number yValue = (Number) row.get(item.getValueColumn());
Object xValue = row.get(item.getXaxisColumn());
if (sValue.equals(s)) {
// find the index of the x label
result.setValue(xValue.toString(), yValue);
}
}
}
chart = ChartFactory.createPieChart3D(null, // chart title
result, // data
true, // include legend
true, false);
PiePlot3D plot = (PiePlot3D) chart.getPlot();
plot.setStartAngle(290);
plot.setDirection(Rotation.CLOCKWISE);
plot.setBackgroundPaint(Color.decode(item.getBackGroundColour()));
plot.setForegroundAlpha(0.5f);
} else {
throw new ChartDefinitionException ("series not returned");
}
break;
default:
DefaultCategoryDataset dataSet = new DefaultCategoryDataset();
if (StringUtils.isBlank(item.getSeriesNameColumn())) {
for (Map<String, Object> row : values) {
Object o=row.get(item.getValueColumn());
Number yValue;
if (o instanceof String) { //I hate Oracle
yValue=Integer.parseInt(o.toString());
} else {
yValue=(Number)o;
}
Comparable xValue = (Comparable) row.get(item
.getXaxisColumn());
dataSet.addValue(yValue, "Value", xValue);
}
} else {
for (Object s : series) {
for (Map<String, Object> row : values) {
Object sValue = row.get(item.getSeriesNameColumn());
Number yValue = (Number) row.get(item.getValueColumn());
Comparable xValue = (Comparable) row.get(item
.getXaxisColumn());
if (sValue.equals(s)) {
dataSet.addValue(yValue, (Comparable) s, xValue);
}
}
}
}
switch (item.getChartType()) {
case BAR_STACKED_3D:
chart = ChartFactory.createStackedBarChart3D("", "",
item.getYLabel(), dataSet, PlotOrientation.VERTICAL,
true, false, false);
CategoryPlot barPlotStack3D = (CategoryPlot) chart.getPlot();
barPlotStack3D.setDomainGridlinesVisible(item.isGridLines());
barPlotStack3D.setRangeGridlinesVisible(item.isGridLines());
barPlotStack3D.setRangeGridlinePaint(Color.black);
barPlotStack3D.setDomainGridlinePaint(Color.black);
BarRenderer3D brStack3D = (BarRenderer3D) barPlotStack3D
.getRenderer();
brStack3D.setItemMargin(0.0d);
barPlotStack3D.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
//fix for missing x-axis labels
barPlotStack3D.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
case BAR_STACKED:
chart = ChartFactory
.createStackedBarChart(
"",
"",
item.getYLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true,
false, false);
CategoryPlot barPlotStack = (CategoryPlot) chart.getPlot();
BarRenderer brStack = (BarRenderer) barPlotStack.getRenderer();
brStack.setItemMargin(0.0d);
barPlotStack.setDomainGridlinesVisible(item.isGridLines());
barPlotStack.setRangeGridlinesVisible(item.isGridLines());
barPlotStack.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
barPlotStack.setRangeGridlinePaint(Color.black);
barPlotStack.setDomainGridlinePaint(Color.black);
//fix for missing x-axis labels
barPlotStack.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
case BAR_3D:
chart = ChartFactory
.createBarChart3D(
"",
"",
item.getYLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true,
false, false);
CategoryPlot barPlot3D = (CategoryPlot) chart.getPlot();
BarRenderer3D br3D = (BarRenderer3D) barPlot3D.getRenderer();
br3D.setItemMargin(0.0d);
barPlot3D.setDomainGridlinesVisible(item.isGridLines());
barPlot3D.setRangeGridlinesVisible(item.isGridLines());
barPlot3D.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
barPlot3D.setRangeGridlinePaint(Color.black);
barPlot3D.setDomainGridlinePaint(Color.black);
//fix for missing x-axis labels
barPlot3D.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
case BAR:
chart = ChartFactory
.createBarChart(
"",
"",
item.getYLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true,
false, false);
CategoryPlot barPlot = (CategoryPlot) chart.getPlot();
BarRenderer br = (BarRenderer) barPlot.getRenderer();
br.setItemMargin(0.0d);
barPlot.setDomainGridlinesVisible(item.isGridLines());
barPlot.setRangeGridlinesVisible(item.isGridLines());
barPlot.setRangeGridlinePaint(Color.black);
barPlot.setDomainGridlinePaint(Color.black);
barPlot.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
//fix for missing x-axis labels
barPlot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
case AREA:
chart = ChartFactory
.createAreaChart(
"",
"",
item.getYLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true,
false, false);
CategoryPlot areaPlot = (CategoryPlot) chart.getPlot();
areaPlot.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
areaPlot.setDomainGridlinesVisible(item.isGridLines());
areaPlot.setRangeGridlinesVisible(item.isGridLines());
areaPlot.setRangeGridlinePaint(Color.black);
areaPlot.setDomainGridlinePaint(Color.black);
//fix for missing x-axis labels
areaPlot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
case LINE_3D:
chart = ChartFactory
.createLineChart3D(
"",
"",
item.getYLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true,
false, false);
CategoryPlot linePlot3D = (CategoryPlot) chart.getPlot();
linePlot3D.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
linePlot3D.setDomainGridlinesVisible(item.isGridLines());
linePlot3D.setRangeGridlinesVisible(item.isGridLines());
linePlot3D.setRangeGridlinePaint(Color.black);
linePlot3D.setDomainGridlinePaint(Color.black);
//fix for missing x-axis labels
linePlot3D.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
case LINE:
default:
chart = ChartFactory
.createLineChart(
"",
"",
item.getYLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true,
false, false);
CategoryPlot linePlot = (CategoryPlot) chart.getPlot();
linePlot.setBackgroundPaint(Color.decode(item
.getBackGroundColour()));
linePlot.setDomainGridlinesVisible(item.isGridLines());
linePlot.setRangeGridlinesVisible(item.isGridLines());
linePlot.setRangeGridlinePaint(Color.black);
linePlot.setDomainGridlinePaint(Color.black);
//fix for missing x-axis labels
linePlot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
break;
}
}
chart.setAntiAlias(true);
chart.setTextAntiAlias(true);
return chart;
}
private JFreeChart getSamplerChart(RunnerDashboardSampler item ) {
JFreeChart chart;
// noddy security check
if (!doesUserHaveGroup(item.getGroup().getGroupName())) {
throw new SecurityException(
"user does not have permission to group: " + groupName);
}
DefaultCategoryDataset dataSet = new DefaultCategoryDataset();
String timeVal="";
for (SamplingData d : item.getSamplingData()) {
Number yValue = d.getValue();
SimpleDateFormat sdf;
switch (item.getInterval()) {
case DAY:
sdf = new SimpleDateFormat("EEEEE");
break;
case HOUR:
sdf = new SimpleDateFormat("HH:mm:ss");
break;
case MINUTE:
sdf = new SimpleDateFormat("HH:mm:ss");
timeVal= (new SimpleDateFormat("HH")).format(d.getSampleTime());
break;
case MONTH:
sdf = new SimpleDateFormat("MMMMM");
break;
case SECOND:
default:
timeVal= (new SimpleDateFormat("HH:mm")).format(d.getSampleTime());
sdf = new SimpleDateFormat("HH:mm:ss");
}
String xValue=sdf.format(new Date(d.getSampleTime()));
if (item.isRecordTrendData()&&item.getTrendData()!=null) {
dataSet.addValue(yValue, "actual", xValue);
switch (item.getInterval()) {
case DAY:
sdf = new SimpleDateFormat("EEEEE");
break;
case HOUR:
sdf = new SimpleDateFormat("HH");
break;
case MINUTE:
sdf = new SimpleDateFormat("mm");
break;
case MONTH:
sdf = new SimpleDateFormat("MMMMM");
break;
case SECOND:
default:
sdf = new SimpleDateFormat("ss");
}
String timeStringCompare = sdf.format(new Date(d.getSampleTime()));
if (item.isRecordTrendData()&&item.getTrendData()!=null) {
//hack - needs work
for (TrendData t: item.getTrendData()) {
if (t.getTimeString().equals(timeStringCompare)) {
String timeString=t.getTimeString();
switch (item.getInterval()) {
case HOUR:
timeString=timeString+":00:00";
break;
case MINUTE:
timeString=timeVal+":"+timeString+":00";
break;
case SECOND:
timeString=timeVal+":"+timeString;
}
dataSet.addValue(t.getMeanValue(), "mean", timeString);
dataSet.addValue(t.getMaxValue(), "maximum", timeString);
dataSet.addValue(t.getMinValue(), "minimum", timeString);
break;
}
}
}
} else {
dataSet.addValue(yValue, item.getValueColumn(), xValue);
}
}
chart = ChartFactory
.createLineChart(
"",
"",
item.getYAxisLabel(),
dataSet,
item.getOrientation() == Orientation.VERTICAL ? PlotOrientation.VERTICAL
: PlotOrientation.HORIZONTAL, true, false,
false);
CategoryPlot linePlot = (CategoryPlot) chart.getPlot();
linePlot.getRangeAxis().setAutoRange(true);
linePlot.setBackgroundPaint(Color.decode(item.getBackGroundColour()));
linePlot.setDomainGridlinesVisible(item.isGridLines());
linePlot.setRangeGridlinesVisible(item.isGridLines());
linePlot.setRangeGridlinePaint(Color.black);
linePlot.setDomainGridlinePaint(Color.black);
linePlot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
if (item.isRecordTrendData()) {
MinMaxCategoryRenderer renderer = new MinMaxCategoryRenderer();
renderer.setDrawLines(true);
linePlot.setRenderer(renderer);
}
chart.setAntiAlias(true);
chart.setTextAntiAlias(true);
return chart;
}
public Integer getItemId() {
return itemId;
}
public void setItemId(Integer itemId) {
this.itemId = itemId;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}