All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ro.nextreports.engine.chart.JsonExporter Maven / Gradle / Ivy

Go to download

NextReports Engine is a lightweight Java platform development library which can be used to run NextReports inside your applications.

There is a newer version: 9.2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 ro.nextreports.engine.chart;

import java.awt.Color;
import java.awt.Font;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ro.nextreports.engine.exporter.exception.NoDataFoundException;
import ro.nextreports.engine.exporter.util.function.AbstractGFunction;
import ro.nextreports.engine.exporter.util.function.FunctionFactory;
import ro.nextreports.engine.exporter.util.function.FunctionUtil;
import ro.nextreports.engine.exporter.util.function.GFunction;
import ro.nextreports.engine.i18n.I18nUtil;
import ro.nextreports.engine.queryexec.QueryException;
import ro.nextreports.engine.queryexec.QueryResult;
import ro.nextreports.engine.util.StringUtil;
import ro.nextreports.jofc2.model.Text;
import ro.nextreports.jofc2.model.axis.Axis;
import ro.nextreports.jofc2.model.axis.Label;
import ro.nextreports.jofc2.model.axis.Label.Rotation;
import ro.nextreports.jofc2.model.axis.XAxis;
import ro.nextreports.jofc2.model.axis.YAxis;
import ro.nextreports.jofc2.model.axis.YLabel;
import ro.nextreports.jofc2.model.elements.AnimatedElement;
import ro.nextreports.jofc2.model.elements.AreaLineChart;
import ro.nextreports.jofc2.model.elements.BarChart;
import ro.nextreports.jofc2.model.elements.Element;
import ro.nextreports.jofc2.model.elements.HorizontalBarChart;
import ro.nextreports.jofc2.model.elements.LineChart;
import ro.nextreports.jofc2.model.elements.PieChart;
import ro.nextreports.jofc2.model.elements.PieChart.AnimationPie;
import ro.nextreports.jofc2.model.elements.StackedBarChart;
import ro.nextreports.jofc2.model.elements.Tooltip;

/**
 * @author Decebal Suiu
 */
public class JsonExporter implements ChartExporter {

    private OutputStream out;
    private QueryResult result;
    private Chart chart;
    private PrintStream stream;
    // default background color for a flash chart (if none is set)
    private final Color DEFAULT_BACKGROUND = new Color(248, 248, 216);
    private Map parameterValues;
    private String drillFunction;

    private static final String X_KEY = "X_VALUE";
    public static final String X_VALUE = "${" + X_KEY +  "}";
    
    // animation strings
    // http://teethgrinder.co.uk/open-flash-chart-2/bar-chart-on-show.php
    private String POP = "pop";
    private String POP_UP = "pop-up";
    private String DROP = "drop";
    private String FADE_IN = "fade-in";
    private String GROW_UP = "grow-up";      // does not work
    private String GROW_DOWN = "grow-down";  // does not work  -> use grow
    
    // for line
    private String EXPLODE = "explode";
    private String MID_SLIDE= "mid-slide";
    private String SHRINK_IN = "shrink-in";
    
    private String language;
    
    public JsonExporter(Map parameterValues, QueryResult result, OutputStream out,
                        Chart chart, String drillFunction, String language) {
        this.parameterValues = parameterValues;
        this.result = result;
        this.out = out;
        this.chart = chart;
        this.drillFunction = drillFunction;
        this.language = language;
    }

    public boolean export() throws QueryException, NoDataFoundException {
        testForData();

        initExport();

        String json = createFlashChart().toString();           
        stream.print(json);

        return true;
    }

    private void testForData() throws QueryException, NoDataFoundException {
        // for procedure call we do not know the row count (is -1)
        if (this.out == null || result == null
                || result.getColumnCount() <= 0
                || result.getRowCount() == 0) {
            throw new NoDataFoundException();
        }
    }

    protected void initExport() throws QueryException {
        stream = createPrintStream();
    }

    protected PrintStream createPrintStream() throws QueryException {
        try {
            return new PrintStream(out, false, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new QueryException(e);
        }
    }

    private String getStyle(ChartTitle chartTitle) {
        StringBuffer titleStyle = new StringBuffer();
        Font font = chartTitle.getFont();
        titleStyle.append('{');
        titleStyle.append("font-size: ").append(font.getSize()).append("px;");
        titleStyle.append("color: ").append(getHexColor(chartTitle.getColor())).append(";");
        titleStyle.append("font-family: ").append(font.getFamily()).append(";");
        if (font.isBold()) {
            // it's omitted by ofc
            titleStyle.append("font-weight: bold;");
        }
        if (font.isItalic()) {
            // it's omitted by ofc
            titleStyle.append("font-style: italic;");
        }
        byte align = chartTitle.getAlignment();
        if (align == ChartTitle.LEFT_ALIGNMENT) {
            titleStyle.append("text-align: left;");
        } else if (align == ChartTitle.CENTRAL_ALIGNMENT) {
            titleStyle.append("text-align: center;");
        } else if (align == ChartTitle.RIGHT_ALIGNMENT) {
            titleStyle.append("text-align: right;");
        }
        titleStyle.append('}');
        return titleStyle.toString();
    }

    private ro.nextreports.jofc2.model.Chart createFlashChart() throws QueryException {

        ChartTitle chartTitle = chart.getTitle();
        String title = replaceParameters(chartTitle.getTitle());      
        title = StringUtil.getI18nString(title, I18nUtil.getLanguageByName(chart, language));

        String titleStyle = getStyle(chartTitle);
        ro.nextreports.jofc2.model.Chart flashChart = new ro.nextreports.jofc2.model.Chart(title, titleStyle);
        XAxis xAxis = new XAxis();
        xAxis.setColour(getHexColor(chart.getxAxisColor()));
        flashChart.setXAxis(xAxis);
        YAxis yAxis = new YAxis();
        yAxis.setColour(getHexColor(chart.getyAxisColor()));
        flashChart.setYAxis(yAxis);

        setBackground(flashChart);
        setGridAxisColors(flashChart, xAxis, yAxis);
        setLegends(flashChart);
        setTooltip(flashChart);

        boolean showXLabel = chart.getXShowLabel() == null ? false : chart.getXShowLabel();
        boolean showYLabel = chart.getYShowLabel() == null ? false : chart.getYShowLabel();

        setTicks(xAxis, yAxis, showXLabel, showYLabel);
        setElements(flashChart, xAxis, yAxis, showXLabel, showYLabel);
        
        String yTooltipPattern = chart.getYTooltipPattern();
        if (yTooltipPattern != null) {        	
        	DecimalFormat df = new DecimalFormat(yTooltipPattern);
        	int numDecimals = df.getMinimumFractionDigits();
        	char decimalSEparator = df.getDecimalFormatSymbols().getDecimalSeparator();        	
        	flashChart.setNumDecimals(numDecimals);
        	flashChart.setDecimalSeparatorIsComma(',' == decimalSEparator);
        	flashChart.setThousandSeparatorDisabled(false);
        }	
        
        return flashChart;
    }

    private Element[] createBarChart(XAxis xAxis, YAxis yAxis, boolean showXLabel, boolean showYLabel) throws QueryException {

        Element[] charts = new Element[chart.getYColumns().size()];

        for (int i = 0; i < charts.length; i++) {
            BarChart barChart;
            byte style = chart.getType().getStyle();
            switch (style) {
                case ChartType.STYLE_BAR_GLASS:
                    barChart = new BarChart(BarChart.Style.GLASS);
                    break;
                case ChartType.STYLE_BAR_CYLINDER:
                    barChart = new BarChart(BarChart.Style.CYLINDER);
                    break;
                case ChartType.STYLE_BAR_PARALLELIPIPED:
                    barChart = new BarChart(BarChart.Style.THREED);
                    break;
                case ChartType.STYLE_BAR_DOME:
                    barChart = new BarChart(BarChart.Style.DOME);
                    break;
                default:
                    barChart = new BarChart();
                    break;
            }
            barChart.setColour(getHexColor(chart.getForegrounds().get(i)));
            barChart.setAlpha(getAlpha(chart.getTransparency()));
            barChart.setTooltip("#val#");
            barChart.setFontSize(chart.getFont().getSize());
            barChart.setOnShow(new AnimatedElement.OnShow(DROP,0,1));
            charts[i] = barChart;
        }

        createChart(xAxis, yAxis, showXLabel, showYLabel, charts);

        return charts;
    }

    private Element[] createHorizontalBarChart(XAxis xAxis, YAxis yAxis, boolean showXLabel, boolean showYLabel) throws QueryException {
        Element[] charts = new Element[chart.getYColumns().size()];        
        for (int i = 0; i < charts.length; i++) {
            HorizontalBarChart barChart = new HorizontalBarChart();
            barChart.setColour(getHexColor(chart.getForegrounds().get(i)));
            barChart.setAlpha(getAlpha(chart.getTransparency()));
            barChart.setTooltip("#val#");            
            // setting font size on horizontal bar chart will make a json which cannot be rendered by JOFC api!            
            //barChart.setFontSize(chart.getFont().getSize());
            charts[i] = barChart;
        }
        createChart(xAxis, yAxis, showXLabel, showYLabel, charts);

        return charts;
    }


    private Element[] createPieChart(boolean showXLabel, boolean showYLabel) throws QueryException {
        Element[] charts = new Element[1];
        PieChart pieChart = new PieChart();
        pieChart.setAnimate(true);
        pieChart.setStartAngle(35);
        pieChart.setBorder(2);
        List animations = new ArrayList();
        animations.add(new AnimationPie.Fade());
        animations.add(new AnimationPie.Bounce(10));
        pieChart.addAnimations(animations);
        pieChart.setGradientFill(true);
        pieChart.setAlpha(getAlpha(chart.getTransparency()));
        List colors = new ArrayList();        
        for (Color color : chart.getForegrounds()) {
            colors.add(getHexColor(color));
        }        
        pieChart.setColours(colors.toArray(new String[10]));
        pieChart.setTooltip("#val# of #total#
#percent# of 100%"); pieChart.setFontSize(chart.getFont().getSize()); charts[0] = pieChart; createChart(null, null, showXLabel, showYLabel, new Element[]{pieChart}); return charts; } private Element[] createStackedBarChart(XAxis xAxis, YAxis yAxis, boolean showXLabel, boolean showYLabel) throws QueryException { Element[] charts = new Element[chart.getYColumns().size()]; StackedBarChart barChart = new StackedBarChart(); barChart.setAlpha(getAlpha(chart.getTransparency())); barChart.setTooltip("#val#"); barChart.setFontSize(chart.getFont().getSize()); for (int i = 0; i < charts.length; i++) { charts[i] = barChart; } createChart(xAxis, yAxis, showXLabel, showYLabel, charts); return charts; } private Element[] createLineChart(XAxis xAxis, YAxis yAxis, boolean showXLabel, boolean showYLabel) throws QueryException { Element[] charts = new Element[chart.getYColumns().size()]; for (int i = 0; i < charts.length; i++) { LineChart lineChart = new LineChart(); lineChart.setWidth(4); lineChart.setColour(getHexColor(chart.getForegrounds().get(i))); lineChart.setAlpha(getAlpha(chart.getTransparency())); lineChart.setDotSize(5); LineChart.Style.Type type; byte style = chart.getType().getStyle(); switch (style) { case ChartType.STYLE_LINE_DOT_SOLID: type = LineChart.Style.Type.SOLID_DOT; break; case ChartType.STYLE_LINE_DOT_HOLLOW: type = LineChart.Style.Type.HALLOW_DOT; break; case ChartType.STYLE_LINE_DOT_ANCHOR: type = LineChart.Style.Type.ANCHOR; break; case ChartType.STYLE_LINE_DOT_BOW: type = LineChart.Style.Type.BOW; break; case ChartType.STYLE_LINE_DOT_STAR: type = LineChart.Style.Type.STAR; break; default: type = LineChart.Style.Type.DOT; break; } lineChart.setDotStyle(new LineChart.Style(type, "#111111", 4, 4).setRotation(90)); lineChart.setFontSize(chart.getFont().getSize()); lineChart.setOnShow(new AnimatedElement.OnShow(EXPLODE)); charts[i] = lineChart; } createChart(xAxis, yAxis, showXLabel, showYLabel, charts); return charts; } private Element[] createAreaChart(XAxis xAxis, YAxis yAxis, boolean showXLabel, boolean showYLabel) throws QueryException { Element[] charts = new Element[chart.getYColumns().size()]; for (int i = 0; i < charts.length; i++) { AreaLineChart areaChart = new AreaLineChart(); areaChart.setWidth(4); areaChart.setColour(getHexColor(chart.getForegrounds().get(i))); areaChart.setAlpha(getAlpha(chart.getTransparency())); areaChart.setDotSize(5); areaChart.setFontSize(chart.getFont().getSize()); areaChart.setOnShow(new AnimatedElement.OnShow(MID_SLIDE)); charts[i] = areaChart; } createChart(xAxis, yAxis, showXLabel, showYLabel, charts); return charts; } private void createChart(XAxis xAxis, YAxis yAxis, boolean showXLabel, boolean showYLabel, Element[] elementChart) throws QueryException { Number min = Double.MAX_VALUE; Number max = Double.MIN_VALUE; int row = 0; Object previous = null; String xColumn = chart.getXColumn(); String xPattern = chart.getXPattern(); String lastXValue = ""; boolean isPie = elementChart[0] instanceof PieChart; boolean isHorizontal = elementChart[0] instanceof HorizontalBarChart; boolean isStacked = elementChart[0] instanceof StackedBarChart; int chartsNo = elementChart.length; GFunction[] functions = new GFunction[chartsNo]; List keys = new ArrayList(); for (int i = 0; i < chartsNo; i++) { functions[i] = FunctionFactory.getFunction(chart.getYFunction()); if ((chart.getYColumnsLegends() != null) && (i < chart.getYColumnsLegends().size())) { if (chart.getYColumnsLegends().get(i) != null) { if (isStacked) { StackedBarChart.Key key = new StackedBarChart.Key(getHexColor(chart.getForegrounds().get(i)), StringUtil.getI18nString(replaceParameters(chart.getYColumnsLegends().get(i)),I18nUtil.getLanguageByName(chart, language)), 12); keys.add(key); } else { elementChart[i].setText(StringUtil.getI18nString(replaceParameters(chart.getYColumnsLegends().get(i)),I18nUtil.getLanguageByName(chart, language))); } } } } // legends for stacked bar chart if (isStacked) { ((StackedBarChart)elementChart[0]).addKeys(keys); } int groups = 1; HashMap infoLabels = new HashMap(); while (result.hasNext()) { Object[] objects = new Object[chartsNo]; Number[] computedValues = new Number[chartsNo]; for (int i = 0; i < chartsNo; i++) { if (chart.getYColumns().get(i) != null) { objects[i] = result.nextValue(chart.getYColumns().get(i)); Number value = null; if (objects[i] instanceof Number) { value = (Number) objects[i]; } else if (objects[i] != null) { value = 1; } if (value == null) { value = 0; } // open flash chart bug: // if value is of type BigDecimal and chart is StackedBarChart : json has values between "" // and bars are drawn over the title (a good stackedbarchart has not the values between "") // curiously all other chart types have values between "" // so here we assure that values are not BigDecimals computedValues[i] = value.doubleValue(); } } Object xValue; if (row == 0) { xValue = result.nextValue(xColumn); lastXValue = getStringValue(xColumn, xPattern); } else { xValue = previous; } Object newXValue = result.nextValue(xColumn); boolean add = false; // no function : add the value if (AbstractGFunction.NOOP.equals(functions[0].getName())) { lastXValue = getStringValue(xColumn, xPattern); add = true; // compute function } else { boolean equals = FunctionUtil.parameterEquals(xValue, newXValue); if (equals) { for (int i = 0; i < chartsNo; i++) { functions[i].compute(objects[i]); } } else { for (int i = 0; i < chartsNo; i++) { add = true; computedValues[i] = (Number) functions[i].getComputedValue(); functions[i].reset(); functions[i].compute(objects[i]); } } } if (add) { //System.out.println("compValue="+computedValue); Number sum = 0; for (int i = 0; i < chartsNo; i++) { addValue(computedValues[i], lastXValue, elementChart[i], i); if (!isStacked) { min = Math.min(min.doubleValue(), computedValues[i].doubleValue()); max = Math.max(max.doubleValue(), computedValues[i].doubleValue()); } else { sum = sum.doubleValue() + computedValues[i].doubleValue(); } } if ((chart.getStartingFromZero() != null) && chart.getStartingFromZero() && (min.intValue() > 0)) { min = 0; } if (isStacked) { min = 0; max = Math.max(max.doubleValue(), sum.doubleValue()); } infoLabels.put(groups, lastXValue); groups++; //System.out.println("add label : " + lastXValue + " g="+ groups); lastXValue = getStringValue(xColumn, xPattern); } row++; previous = newXValue; } // last group if (!AbstractGFunction.NOOP.equals(functions[0].getName())) { Number sum = 0; for (int i = 0; i < chartsNo; i++) { Number value = (Number) functions[i].getComputedValue(); addValue(value, lastXValue, elementChart[i], i); if (!isStacked) { min = Math.min(min.doubleValue(), value.doubleValue()); max = Math.max(max.doubleValue(), value.doubleValue()); } else { sum = sum.doubleValue() + value.doubleValue(); } } if ((chart.getStartingFromZero() != null) && chart.getStartingFromZero() && (min.intValue() > 0)) { min = 0; } if (isStacked) { min = 0; max = Math.max(max.doubleValue(), sum.doubleValue()); } infoLabels.put(groups, lastXValue); } else { groups--; } // info labels if (!isPie) { // for horizontal charts (horizontal bars) if (isHorizontal) { int factor = groups < 2 ? 0 : (groups - 2) / 10; YRange range = setAxisRange(yAxis, 1, groups - 0.5); for (int i = groups; i > 0; i--) { //System.out.println("add label : " + infoLabels.get(i) + " pos="+ (groups - i)); String text = infoLabels.get(i); if (!showXLabel) { text = ""; } addYLabel(yAxis, text, groups - i + factor + 1); for (int j=1; j




© 2015 - 2024 Weber Informatics LLC | Privacy Policy