package org.toobsframework.pres.chart; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberAxis3D; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.CombinedDomainCategoryPlotEx; import org.jfree.chart.plot.CombinedRangeCategoryPlotEx; import org.jfree.chart.plot.MultiCategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.SpiderWebPlot; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.DatasetGroup; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.toobsframework.pres.chart.config.BasePlot; import org.toobsframework.pres.chart.config.Dataset; import org.toobsframework.pres.chart.config.DatasetSeries; import org.toobsframework.pres.chart.config.DomainAxisDef; import org.toobsframework.pres.chart.config.RangeAxisDef; import org.toobsframework.pres.chart.config.types.AxisDefNumberFormaterType; import org.toobsframework.exception.ParameterException; import org.toobsframework.pres.component.datasource.api.DataSourceNotInitializedException; import org.toobsframework.pres.component.datasource.api.IDataSource; import org.toobsframework.pres.component.datasource.api.InvalidSearchContextException; import org.toobsframework.pres.component.datasource.api.InvalidSearchFilterException; import org.toobsframework.pres.component.datasource.api.ObjectCreationException; import org.toobsframework.pres.util.ParameterUtil; import org.toobsframework.util.Configuration; import org.toobsframework.util.IRequest; @SuppressWarnings("unchecked") public class ChartBuilder implements BeanFactoryAware { private static Log log = LogFactory.getLog(ChartBuilder.class); private BeanFactory beanFactory; public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } private IDataSource datasource; public String buildAsImage(ChartDefinition chartDef, IRequest componentRequest, int width, int height) throws ChartException { JFreeChart chart = this.build(chartDef, componentRequest); if (width <= 0) chartDef.getChartWidth(); if (height <= 0) chartDef.getChartHeight(); String genFileName = chartDef.getId() + "-" + new Date().getTime() + ".png"; String imageOutputFileName = Configuration.getInstance().getUploadDir() + genFileName; try { File imageOutputFile = new File(imageOutputFileName); OutputStream os = null; ChartRenderingInfo chartRenderingInfo = new ChartRenderingInfo(); try { os = new FileOutputStream(imageOutputFile); ChartUtilities.writeChartAsPNG(os, chart, width, height, chartRenderingInfo); } finally { if (os != null) { os.close(); } } } catch (FileNotFoundException e) { throw new ChartException(e); } catch (IOException e) { throw new ChartException(e); } return imageOutputFileName; } public JFreeChart build(ChartDefinition chartDef, IRequest componentRequest) throws ChartException { JFreeChart chart = null; try { Map params = componentRequest.getParams(); if(chartDef.getParameters() != null){ ParameterUtil.mapParameters("Chart:AreaChart:" + chartDef.getId(), chartDef.getParameters().getParameter(), params, params, chartDef.getId(), null); } Plot plot = configurePlot(chartDef.getId(), chartDef.getPlot(), params); chart = finishChart(chartDef, plot, params); } catch (ParameterException e) { log.error("Chart build exception " + e.getMessage(), e); throw new ChartException(e); } return chart; } private Plot configurePlot(String id, org.toobsframework.pres.chart.config.Plot plotDef, Map params) throws ChartException { Plot plot = null; if (plotDef.getSubPlotCount() > 0) { boolean is3D = (ParameterUtil.resolveParam(plotDef.getIs3D(), params, "false")[0].equals("false") ? false : true); int plotType = ChartUtil.getSupportedPlots().get(ParameterUtil.resolveParam(plotDef.getType(), params, "multiCategory")[0]); PlotOrientation orientation = (ParameterUtil.resolveParam(plotDef.getOrientation(), params, "vertical")[0].equals("horizontal") ? PlotOrientation.HORIZONTAL : PlotOrientation.VERTICAL); switch (plotType) { case ChartUtil.PLOT_MULTICATEGORY_TYPE: plot = new MultiCategoryPlot(); for (int p = 0; p<plotDef.getSubPlotCount(); p++) { ((MultiCategoryPlot)plot).add((CategoryPlot)this.configurePlot(id, plotDef.getSubPlot(p), params, true, plotType, plotDef)); } ((MultiCategoryPlot)plot).setOrientation(orientation); ((MultiCategoryPlot)plot).setGap(plotDef.getGap()); if (plotDef.getInsets() != null) { plot.setInsets(ChartUtil.getRectangle(plotDef.getInsets(), params)); } break; case ChartUtil.PLOT_COMBINEDDOMAINCATEGORY_TYPE: CategoryAxis domainAxis = ChartUtil.createCategoryAxis(plotDef.getDomainAxisDef(), params, is3D); plot = new CombinedDomainCategoryPlotEx(domainAxis); for (int p = 0; p<plotDef.getSubPlotCount(); p++) { ((CombinedDomainCategoryPlotEx)plot).add((CategoryPlot)this.configurePlot(id, plotDef.getSubPlot(p), params, true, plotType, plotDef)); } ((CombinedDomainCategoryPlotEx)plot).setOrientation(orientation); ((CombinedDomainCategoryPlotEx)plot).setGap(plotDef.getGap()); if (plotDef.getInsets() != null) { plot.setInsets(ChartUtil.getRectangle(plotDef.getInsets(), params)); } break; case ChartUtil.PLOT_COMBINEDRANGECATEGORY_TYPE: ValueAxis rangeAxis = createValueAxis(plotDef.getRangeAxisDef(), params, is3D); plot = new CombinedRangeCategoryPlotEx(rangeAxis); for (int p = 0; p<plotDef.getSubPlotCount(); p++) { ((CombinedRangeCategoryPlotEx)plot).add((CategoryPlot)this.configurePlot(id, plotDef.getSubPlot(p), params, true, plotType, plotDef)); } ((CombinedRangeCategoryPlotEx)plot).setOrientation(orientation); if (plotDef.getInsets() != null) { plot.setInsets(ChartUtil.getRectangle(plotDef.getInsets(), params)); } break; } } else { plot = this.configurePlot(id, plotDef, params, false, -1, null); } return plot; } private Plot configurePlot(String id, BasePlot plotDef, Map params, boolean isSubPlot, int parentPlotType, BasePlot parentPlot) throws ChartException { boolean is3D = (ParameterUtil.resolveParam(plotDef.getIs3D(), params, "false")[0].equals("false") ? false : true); Integer plotType = ChartUtil.getSupportedPlots().get(ParameterUtil.resolveParam(plotDef.getType(), params, "multiCategory")[0]); if (plotType == null) { throw new ChartException("Unsupported Plot type " + ParameterUtil.resolveParam(plotDef.getType(), params, "multiCategory")[0]); } Plot plot = null; switch (plotType) { case ChartUtil.PLOT_CATEGORY_TYPE: DomainAxisDef domainAxis = null; RangeAxisDef rangeAxis = null; plot = new CategoryPlot(); if (isSubPlot) { if (plotDef.getDomainAxisDef() != null && parentPlotType != ChartUtil.PLOT_COMBINEDDOMAINCATEGORY_TYPE) { domainAxis = plotDef.getDomainAxisDef(); } else if (parentPlotType != ChartUtil.PLOT_COMBINEDDOMAINCATEGORY_TYPE) { domainAxis = parentPlot.getDomainAxisDef(); } if (plotDef.getRangeAxisDef() != null && parentPlotType != ChartUtil.PLOT_COMBINEDRANGECATEGORY_TYPE) { rangeAxis = plotDef.getRangeAxisDef(); } else if (parentPlotType != ChartUtil.PLOT_COMBINEDRANGECATEGORY_TYPE) { rangeAxis = parentPlot.getRangeAxisDef(); } } else { domainAxis = plotDef.getDomainAxisDef(); rangeAxis = plotDef.getRangeAxisDef(); } ((CategoryPlot)plot).setDomainAxis(ChartUtil.createCategoryAxis(domainAxis, params, is3D)); ((CategoryPlot)plot).setRangeAxis(createValueAxis(rangeAxis, params, is3D)); for (int g = 0; g < plotDef.getDatasetGroupCount(); g++) { org.toobsframework.pres.chart.config.DatasetGroup group = plotDef.getDatasetGroup(g); CategoryItemRenderer renderer = (CategoryItemRenderer)ChartUtil.getRenderer(plotDef, group, params); if (group.getUrlBase() != null) { renderer.setBaseItemURLGenerator(new StandardCategoryURLGenerator(group.getUrlBase())); } ((CategoryPlot)plot).setRenderer(g, renderer); DefaultCategoryDataset categoryDataset = new DefaultCategoryDataset(); if (group.getId() != null) { DatasetGroup datasetGroup = new DatasetGroup(group.getId()); categoryDataset.setGroup(datasetGroup); } for (int i = 0; i < group.getDatasetCount(); i++) { Dataset dataset = group.getDataset(i); generateCategoryDataset(id, categoryDataset, dataset, params); this.setValueAxisBounds(((CategoryPlot)plot).getRangeAxis(), rangeAxis, params); } ((CategoryPlot)plot).setDataset(g, categoryDataset); } ChartUtil.configurePlot(plot, plotDef, domainAxis, rangeAxis, params); break; case ChartUtil.PLOT_XY_TYPE: plot = new XYPlot(); break; case ChartUtil.PLOT_SPIDER_TYPE: plot = new SpiderWebPlot(); DefaultCategoryDataset categoryDataset = new DefaultCategoryDataset(); for (int g = 0; g < plotDef.getDatasetGroupCount(); g++) { org.toobsframework.pres.chart.config.DatasetGroup group = plotDef.getDatasetGroup(g); if (group.getUrlBase() != null) { ((SpiderWebPlot)plot).setURLGenerator(new StandardCategoryURLGenerator(group.getUrlBase())); } if (group.getId() != null) { DatasetGroup datasetGroup = new DatasetGroup(group.getId()); categoryDataset.setGroup(datasetGroup); } for (int i = 0; i < group.getDatasetCount(); i++) { Dataset dataset = group.getDataset(i); generateCategoryDataset(id, categoryDataset, dataset, params); for (int s = 0; s < dataset.getDatasetSeriesCount(); s++) { DatasetSeries series = dataset.getDatasetSeries(s); if (series.getColor() != null) { ((SpiderWebPlot)plot).setSeriesPaint(i + s, ChartUtil.getColor(series.getColor())); } } } } ((SpiderWebPlot)plot).setDataset(categoryDataset); ChartUtil.configurePlot(plot, plotDef, null, null, params); break; } return plot; } protected CategoryDataset generateCategoryDataset(String id, DefaultCategoryDataset categoryDataset, Dataset dataset, Map params) throws ChartException { Map outParams = new HashMap(); Map curParams; curParams = new HashMap(); curParams.putAll(params); ArrayList dataList = (ArrayList)this.datasetSearch(id, dataset, curParams, outParams); for (int j = 0; j < dataList.size(); j++) { Object currentRow = dataList.get(j); for (int s = 0; s <dataset.getDatasetSeriesCount(); s++) { DatasetSeries series = dataset.getDatasetSeries(s); String seriesName = ParameterUtil.resolveParam(series.getName(), params, "Series " + (j+s))[0]; categoryDataset.addValue( Double.parseDouble(String.valueOf(ChartUtil.getDatasetValue(currentRow, series.getValueElement(), new Integer(0)))), String.valueOf(ChartUtil.getDatasetValue(currentRow, series.getRowElement(), seriesName)), String.valueOf(ChartUtil.getDatasetValue(currentRow, series.getColumnElement(), seriesName)) ); } } params.putAll(outParams); return categoryDataset; } protected Collection datasetSearch(String id, Dataset dataset, Map params, Map outParams) throws ChartException { try { if(dataset.getParameters() != null){ ParameterUtil.mapParameters("Component:" + id + ":Dataset:" + dataset.getDaoObject(), dataset.getParameters().getParameter(), params, params, id, null); } return this.datasource.search( dataset.getReturnedValueObject(), ParameterUtil.resolveParam(dataset.getDaoObject(), params)[0], dataset.getSearchCriteria(), dataset.getSearchMethod(), "read", params, outParams); } catch (ParameterException e) { log.error("Chart data search exception " + e.getMessage(), e); throw new ChartException(e); } catch (ObjectCreationException e) { log.error("Chart data search exception " + e.getMessage(), e); throw new ChartException(e); } catch (InvalidSearchContextException e) { log.error("Chart data search exception " + e.getMessage(), e); throw new ChartException(e); } catch (InvalidSearchFilterException e) { log.error("Chart data search exception " + e.getMessage(), e); throw new ChartException(e); } catch (DataSourceNotInitializedException e) { log.error("Chart data search exception " + e.getMessage(), e); throw new ChartException(e); } } public ValueAxis createValueAxis(RangeAxisDef valueAxisDef, Map params, boolean is3D) { ValueAxis valueAxis; if (is3D) { valueAxis = new NumberAxis3D(); } else { valueAxis = new NumberAxis(); } if (valueAxisDef != null) { if (valueAxisDef.getRangeLabel() != null) { valueAxis.setLabel(ChartUtil.evaluateTextLabel(valueAxisDef.getRangeLabel(), params)); if (valueAxisDef.getRangeLabel().getFont() != null) { valueAxis.setLabelFont(ChartUtil.getFont(valueAxisDef.getRangeLabel(), null)); } valueAxis.setLabelPaint(ChartUtil.getColor(valueAxisDef.getRangeLabel().getColor())); } if (valueAxisDef.getIntegerTicks()) { valueAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); } if ( valueAxisDef.getNumberFormater() != null) { switch (valueAxisDef.getNumberFormater().getType()) { case AxisDefNumberFormaterType.PERCENT_TYPE: ((NumberAxis)valueAxis).setNumberFormatOverride(NumberFormat.getPercentInstance()); break; case AxisDefNumberFormaterType.CUSTOMBEAN_TYPE: ((NumberAxis)valueAxis).setNumberFormatOverride((NumberFormat)beanFactory.getBean(valueAxisDef.getCustomFormatBean())); break; } } } return valueAxis; } public void setValueAxisBounds(ValueAxis valueAxis, RangeAxisDef valueAxisDef, Map params) { if (valueAxisDef.getUpperBound() != null) { Double upper = Double.parseDouble( ParameterUtil.resolveParam(valueAxisDef.getUpperBound(), params, "0.0")[0]); if (valueAxis == null || valueAxis.getUpperBound() < upper) valueAxis.setUpperBound(upper); } if (valueAxisDef.getLowerBound() != null) { Double lower = Double.parseDouble( ParameterUtil.resolveParam(valueAxisDef.getLowerBound(), params, "0.0")[0]); if (valueAxis == null || valueAxis.getLowerBound() < lower) valueAxis.setLowerBound(lower); } double lowerMargin = Double.parseDouble( ParameterUtil.resolveParam(valueAxisDef.getLowerMargin(), params, "0.0")[0] ); double upperMargin = Double.parseDouble( ParameterUtil.resolveParam(valueAxisDef.getUpperMargin(), params, "0.0")[0] ); valueAxis.setLowerMargin(lowerMargin); valueAxis.setUpperMargin(upperMargin); } private JFreeChart finishChart(ChartDefinition chartDef, Plot plot, Map params) { JFreeChart chart = new JFreeChart( ChartUtil.evaluateTextLabel(chartDef.getTitle(), params), ChartUtil.getFont(chartDef.getTitle(), JFreeChart.DEFAULT_TITLE_FONT), plot, chartDef.isShowLegend()); if (chartDef.getSubtitle() != null) { TextTitle subtitle = new TextTitle(ChartUtil.evaluateTextLabel(chartDef.getSubtitle(), params)); if (chartDef.getSubtitle().getFont() != null) { subtitle.setFont(ChartUtil.getFont(chartDef.getSubtitle(), null)); } subtitle.setPosition(ChartUtil.getPosition(chartDef.getSubtitle().getPosition())); subtitle.setPadding(ChartUtil.getRectangle(chartDef.getSubtitle().getPadding(), params)); subtitle.setVerticalAlignment(ChartUtil.getVerticalAlignment(chartDef.getSubtitle().getVerticalAlignment())); subtitle.setPaint(ChartUtil.getColor(chartDef.getSubtitle().getColor())); chart.addSubtitle(subtitle); } chart.setBackgroundPaint(ChartUtil.getColor(chartDef.getBackgroundColor())); if (chartDef.getTitle() != null && chartDef.getTitle().getColor() != null) { chart.getTitle().setPaint(ChartUtil.getColor(chartDef.getTitle().getColor())); } if (chartDef.isShowLegend()) { ChartUtil.configureLegend(chart, chartDef.getLegend(), params); } return chart; } public IDataSource getDatasource() { return datasource; } public void setDatasource(IDataSource datasource) { this.datasource = datasource; } }