/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.activiti.explorer.ui.reports;
import java.util.Iterator;
import org.activiti.engine.ActivitiException;
import org.activiti.explorer.ExplorerApp;
import org.activiti.explorer.Messages;
import org.activiti.explorer.ui.mainlayout.ExplorerLayout;
import org.dussan.vaadin.dcharts.DCharts;
import org.dussan.vaadin.dcharts.base.elements.XYaxis;
import org.dussan.vaadin.dcharts.base.elements.XYseries;
import org.dussan.vaadin.dcharts.base.renderers.MarkerRenderer;
import org.dussan.vaadin.dcharts.data.DataSeries;
import org.dussan.vaadin.dcharts.data.Ticks;
import org.dussan.vaadin.dcharts.metadata.LegendPlacements;
import org.dussan.vaadin.dcharts.metadata.XYaxes;
import org.dussan.vaadin.dcharts.metadata.renderers.AxisRenderers;
import org.dussan.vaadin.dcharts.metadata.renderers.LabelRenderers;
import org.dussan.vaadin.dcharts.metadata.renderers.SeriesRenderers;
import org.dussan.vaadin.dcharts.metadata.styles.MarkerStyles;
import org.dussan.vaadin.dcharts.options.Axes;
import org.dussan.vaadin.dcharts.options.AxesDefaults;
import org.dussan.vaadin.dcharts.options.Highlighter;
import org.dussan.vaadin.dcharts.options.Legend;
import org.dussan.vaadin.dcharts.options.Options;
import org.dussan.vaadin.dcharts.options.Series;
import org.dussan.vaadin.dcharts.options.SeriesDefaults;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
/**
* @author Joram Barrez
*/
public class ChartGenerator {
public static final String CHART_TYPE_BAR_CHART = "barChart";
public static final String CHART_TYPE_PIE_CHART = "pieChart";
public static final String CHART_TYPE_LINE_CHART = "lineChart";
public static final String CHART_TYPE_LIST = "list";
public static ChartComponent generateChart(byte[] reportData) {
// Convert json to pojo
JsonNode jsonNode = convert(reportData);
// Title
JsonNode titleNode = jsonNode.get("title");
String title = null;
if (titleNode != null) {
title = titleNode.textValue();
}
ChartComponent chartComponent = new ChartComponent(title);
// Retrieve data sets
JsonNode datasetsNode = jsonNode.get("datasets");
// If no data was returned
if (datasetsNode.size() == 0) {
chartComponent.addChart(null, null, ExplorerApp.get().getI18nManager().getMessage(Messages.REPORTING_ERROR_NOT_ENOUGH_DATA));
return chartComponent;
}
if (datasetsNode != null && datasetsNode.isArray()) {
Iterator<JsonNode> dataIterator = datasetsNode.iterator();
while (dataIterator.hasNext()) {
JsonNode datasetNode = dataIterator.next();
JsonNode descriptionNode = datasetNode.get("description");
String description = null;
if (descriptionNode != null) {
description = descriptionNode.textValue();
}
JsonNode dataNode = datasetNode.get("data");
if (dataNode == null || dataNode.size() == 0) {
chartComponent.addChart(description, null, ExplorerApp.get().getI18nManager().getMessage(Messages.REPORTING_ERROR_NOT_ENOUGH_DATA));
} else {
String[] names = new String[dataNode.size()];
Number[] values = new Number[dataNode.size()];
int index = 0;
Iterator<String> fieldIterator = dataNode.fieldNames();
while(fieldIterator.hasNext()) {
String field = fieldIterator.next();
names[index] = field;
values[index] = dataNode.get(field).numberValue();
index++;
}
// Generate chart (or 'no data' message)
if (names.length > 0) {
Component chart = createChart(datasetNode, names, values);
chartComponent.addChart(description, chart, null);
} else {
chartComponent.addChart(description, null, ExplorerApp.get().getI18nManager().getMessage(Messages.REPORTING_ERROR_NOT_ENOUGH_DATA));
}
}
}
}
return chartComponent;
}
protected static Component createChart(JsonNode dataNode, String[] names, Number[] values) {
String type = dataNode.get("type").textValue();
JsonNode xAxisNode = dataNode.get("xaxis");
String xAxis = null;
if (xAxisNode != null) {
xAxis = xAxisNode.textValue();
}
JsonNode yAxisNode = dataNode.get("yaxis");
String yAxis = null;
if (yAxisNode != null) {
yAxis = yAxisNode.textValue();
}
Component chart = null;
if (CHART_TYPE_BAR_CHART.equals(type)) {
DataSeries dataSeries = new DataSeries().add((Object[]) values);
SeriesDefaults seriesDefaults = new SeriesDefaults().setRenderer(SeriesRenderers.BAR);
Axes axes = new Axes().addAxis(new XYaxis().setRenderer(AxisRenderers.CATEGORY).setTicks(new Ticks().add((Object[]) names)));
Highlighter highlighter = new Highlighter().setShow(false);
Options options = new Options().setSeriesDefaults(seriesDefaults).setAxes(axes).setHighlighter(highlighter);
options.setAnimate(true);
options.setAnimateReplot(true);
chart = new DCharts().setDataSeries(dataSeries).setOptions(options);
} else if(CHART_TYPE_PIE_CHART.equals(type)) {
DataSeries dataSeries = new DataSeries().newSeries();
for (int i=0; i<names.length; i++) {
dataSeries.add(names[i], values[i]);
}
SeriesDefaults seriesDefaults = new SeriesDefaults().setRenderer(SeriesRenderers.PIE);
Options options = new Options().setSeriesDefaults(seriesDefaults);
options.setAnimate(true);
options.setAnimateReplot(true);
Legend legend = new Legend().setShow(true).setPlacement(LegendPlacements.INSIDE);
options.setLegend(legend);
Highlighter highlighter = new Highlighter().setShow(true);
options.setHighlighter(highlighter);
chart = new DCharts().setDataSeries(dataSeries).setOptions(options);
} else if (CHART_TYPE_LINE_CHART.equals(type)) {
AxesDefaults axesDefaults = new AxesDefaults().setLabelRenderer(LabelRenderers.CANVAS);
Axes axes = new Axes()
.addAxis(new XYaxis().setLabel(xAxis != null ? xAxis : "").setMin(names[0]).setMax(names[values.length - 1]).setDrawMajorTickMarks(true))
.addAxis(new XYaxis(XYaxes.Y).setLabel(yAxis != null ? yAxis : "").setDrawMajorTickMarks(true));
Options options = new Options().setAxesDefaults(axesDefaults).setAxes(axes);
DataSeries dataSeries = new DataSeries().newSeries();
for (int i=0; i<names.length; i++) {
// if (parseLong(names[i]) != null) {
// dataSeries.add(parseLong(names[i]), values[i]);
// } else if (parseDouble(names[i]) != null) {
// dataSeries.add(parseDouble(names[i]), values[i]);
// } else {
// dataSeries.add(names[i], values[i]);
// }
dataSeries.add(names[i], values[i]);
}
Series series = new Series().addSeries(
new XYseries().setShowLine(true).setMarkerOptions(new MarkerRenderer().setShadow(true).setSize(7).setStyle(MarkerStyles.CIRCLE)));
options.setSeries(series);
options.setAnimate(true);
options.setAnimateReplot(true);
Highlighter highlighter = new Highlighter().setShow(true);
options.setHighlighter(highlighter);
chart = new DCharts().setDataSeries(dataSeries).setOptions(options);
} else if (CHART_TYPE_LIST.equals(type)) {
GridLayout grid = new GridLayout(2, names.length);
grid.setSpacing(true);
for (int i=0; i<names.length; i++) {
String name = names[i];
Label nameLabel = new Label(name);
nameLabel.addStyleName(ExplorerLayout.STYLE_LABEL_BOLD);
grid.addComponent(nameLabel, 0, i);
Number value = values[i];
Label valueLabel = new Label(value + "");
grid.addComponent(valueLabel, 1, i);
}
chart = grid;
}
if (chart instanceof DCharts) {
// Needed, otherwise the chart will not be shown
((DCharts) chart).show();
}
return chart;
}
protected static JsonNode convert(byte[] jsonBytes) {
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readTree(jsonBytes);
} catch (Exception e) {
throw new ActivitiException("Report dataset contains invalid json", e);
}
}
protected static Long parseLong(String s) {
try {
Long value = Long.parseLong(s);
return value;
} catch (NumberFormatException e) {
return null;
}
}
protected static Double parseDouble(String s) {
try {
Double value = Double.parseDouble(s);
return value;
} catch (NumberFormatException e) {
return null;
}
}
}