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

ro.nextreports.engine.chart.JsonHTML5Exporter 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.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.ColorUtil;
import ro.nextreports.engine.util.ObjectCloner;
import ro.nextreports.engine.util.StringUtil;

/* 
 * @author Mihai Dinca-Panaitescu 
 */
public class JsonHTML5Exporter 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 NextChart nc;
    private String language;

    // markup for clicked value inside onClick javascript function
    private static final String CLICKED_VALUE = "#val";            
    
    public JsonHTML5Exporter(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;
        this.nc = new NextChart();
    }

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

        initExport();

        String json = createHTML5Chart();   
        System.out.println(json);
        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 NextChartTitle createTitle(ChartTitle chartTitle) {
        NextChartTitle nct = new NextChartTitle(StringUtil.getI18nString(chartTitle.getTitle(), I18nUtil.getLanguageByName(chart, language)));
        Font font = chartTitle.getFont();
        
        if (chartTitle.getColor() !=  null) {
        	nct.setColor(ColorUtil.getHexColor(chartTitle.getColor()));
        }
        nct.setFont(createFont(font));
        
        byte align = chartTitle.getAlignment();
        if (align == ChartTitle.LEFT_ALIGNMENT) {
            nct.setAlignment(NextChart.Alignment.left);
        } else if (align == ChartTitle.CENTRAL_ALIGNMENT) {
        	nct.setAlignment(NextChart.Alignment.center);
        } else if (align == ChartTitle.RIGHT_ALIGNMENT) {
        	nct.setAlignment(NextChart.Alignment.right);
        }
        return nct;        
    }
    
    private NextChartFont createFont(Font font) {
    	 String weight = "normal";
         if (font.isBold()) {
             weight = "bold";
         }
         if (font.isItalic()) {
             weight += " italic";
         }
         return new NextChartFont(weight, font.getSize(), font.getFamily());
    }

    private String createHTML5Chart() throws QueryException {
    	
    	setType();
    	setStyle();
    	setBackground();
    	setLabelOrientation(chart.getXorientation());
        setTitle();
                
        if (drillFunction != null) {               	
            nc.setOnClick(drillFunction);        	
        }             
        
        nc.setAlpha(getAlpha(chart.getTransparency()));
				
        if (chart.getxAxisColor() != null) {
        	nc.setColorXaxis(ColorUtil.getHexColor(chart.getxAxisColor()));
        }
        if (chart.getyAxisColor() != null) {
        	nc.setColorYaxis(ColorUtil.getHexColor(chart.getyAxisColor()));
        }
		boolean showGridX = chart.getXShowGrid() == null ? false : chart.getXShowGrid();
	    boolean showGridY = chart.getYShowGrid() == null ? false :chart.getYShowGrid();
		nc.setShowGridX(showGridX);
		nc.setShowGridY(showGridY);
		if (chart.getXGridColor() != null) {
			nc.setColorGridX(ColorUtil.getHexColor(chart.getXGridColor()));
		}
		if (chart.getYGridColor() != null) {
			nc.setColorGridY(ColorUtil.getHexColor(chart.getYGridColor()));
		}
		if (chart.getShowDualAxis() != null) {
			nc.setDualYaxis(chart.getShowDualAxis());
			if (chart.getY2SeriesCount() != null) {
				nc.setY2Count(chart.getY2SeriesCount());
			}
		}
		
		// todo : customize tickCount?
		nc.setTickCount(5);
		
		if ((chart.getTooltipMessage() != null) && ! chart.getTooltipMessage().trim().isEmpty()) {
			String msg = StringUtil.getI18nStringMultiple(chart.getTooltipMessage(), I18nUtil.getLanguageByName(chart, language));
			nc.setMessage(msg);
		}
		
		boolean startingFromZero = chart.getStartingFromZero() == null ? false : chart.getStartingFromZero();
		nc.setStartingFromZero(startingFromZero);

        boolean showXLabel = chart.getXShowLabel() == null ? false : chart.getXShowLabel();
        boolean showYLabel = chart.getYShowLabel() == null ? false : chart.getYShowLabel();
        nc.setShowTicks(showYLabel);
        if (showXLabel) {
        	NextChartAxis xData = new NextChartAxis();
        	xData.setColor(ColorUtil.getHexColor(chart.getXColor()));		
        	xData.setFont(createFont(chart.getXLabelFont()));
        	nc.setxData(xData);
        }
        if (showYLabel) {
			NextChartAxis yData = new NextChartAxis();
			yData.setColor(ColorUtil.getHexColor(chart.getYColor()));
			yData.setFont(createFont(chart.getYLabelFont()));
			nc.setyData(yData);
        }
                       
        String yTooltipPattern = chart.getYTooltipPattern();
        if (yTooltipPattern != null) {        	
        	DecimalFormat df = new DecimalFormat(yTooltipPattern);
        	int decimals = df.getMinimumFractionDigits();
        	char decimalSeparator = df.getDecimalFormatSymbols().getDecimalSeparator(); 
        	char thousandSeparator = df.getDecimalFormatSymbols().getGroupingSeparator();
        	NextNumberFormat tooltipPattern = new NextNumberFormat();
        	tooltipPattern.setDecimals(decimals);
        	tooltipPattern.setDecimalSeparator(String.valueOf(decimalSeparator));
        	tooltipPattern.setThousandSeparator(String.valueOf(thousandSeparator));
        	nc.setTooltipPattern(tooltipPattern);
        }	
        
        
        boolean isCombo = ChartType.isCombo(chart.getType().getType());        
        
        setLegends(isCombo);
        setColors(isCombo);
        
        List> dataList = new ArrayList>();
        int size = chart.getYColumns().size();
        if (isCombo) {
        	// last column is line
        	size = chart.getYColumns().size()-1;
        	if (size > 0) {
        		List> lineDataList = new ArrayList>();        	
        		lineDataList.add(new ArrayList());    
        		nc.setLineData(lineDataList);
        	} else {
        		// is only one y column, put it to data (we cannot have a combo)        		
                dataList.add(new ArrayList());                
        	}
        }        
        for (int i=0; i());
        }
                
        nc.setData(dataList);        
        nc.setLabels(new ArrayList());
        
        createChart(showXLabel, showYLabel);
        
        return nc.toJson();
    }

      
    private void createChart(boolean showXLabel, boolean showYLabel) 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 = "";
       
        int chartsNo = chart.getYColumns().size();  
        boolean isStacked = nc.getType().equals(NextChart.Type.stackedbar) || nc.getType().equals(NextChart.Type.hstackedbar);         
        
        GFunction[] functions = new GFunction[chartsNo];     
        for (int i = 0; i < chartsNo; i++) {
            functions[i] = FunctionFactory.getFunction(chart.getYFunction());
        }    
        int groups = 1;
        HashMap infoLabels = new HashMap();
        List categories = new ArrayList();
        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;
                    String sv = null;
                    if (objects[i] instanceof Number) {
                        value = (Number) objects[i];
                    } else if (objects[i] != null) {
                    	if (ChartType.BUBBLE == chart.getType().getType()) {
                    		sv = (String)objects[i];
                    	} 
                    	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();  
                    if (sv != null) {
                    	categories.add(sv);
                    }
                }
            }

            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, 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 (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, i);                
                if (!isStacked) {
                    min = Math.min(min.doubleValue(), value.doubleValue());
                    max = Math.max(max.doubleValue(), value.doubleValue());
                } else {
                    sum = sum.doubleValue() + value.doubleValue();
                }
            }
            if (isStacked) {
                min = 0;
                max = Math.max(max.doubleValue(), sum.doubleValue());
            }

            infoLabels.put(groups, lastXValue);
        } else {
            groups--;
        }               

		// info labels		
		for (int i = 1; i <= groups; i++) {
			String text = infoLabels.get(i);				
			nc.getLabels().add(text);			
			nc.setShowLabels(showXLabel);			
		}
		
		if (!categories.isEmpty()) {
			nc.setCategories(categories);
		}
		
    }       
   

    private void addValue(Number value, String text, int position) {        	          
        if (ChartType.isCombo(chart.getType().getType())) {
        	if ((position == nc.getData().size()) && (nc.getData().size() > 1)) {
        		nc.getLineData().get(0).add(value); 
        	} else {
        		nc.getData().get(position).add(value);  
        	}
        } else {
        	nc.getData().get(position).add(value);  
        	//System.out.println("add " + position + "  value=" + value);
        }
    }
    
    private void setTitle() {
    	ChartTitle chartTitle = ObjectCloner.silenceDeepCopy(chart.getTitle());    	
        String title = replaceParameters(chartTitle.getTitle());
        chartTitle.setTitle(title);
        if (!isEmpty(title)) {
        	nc.setTitle(createTitle(chartTitle));
        }
    }
   

    private void setBackground() {
        if (chart.getBackground() != null) {
        	nc.setBackground(ColorUtil.getHexColor(chart.getBackground()));
        }
    }  
    
    private void setColors(boolean isCombo) {
    	List colors = new ArrayList();
    	if (ChartType.PIE == chart.getType().getType()) {
    		for (Color color : chart.getForegrounds()) {
    			if (color != null) {
    				colors.add(ColorUtil.getHexColor(color));
    			}
            }    
    	} else {
    		int size = chart.getYColumns().size();
			if (isCombo) {
				if (size > 1) {
					size--;
					List lineColorList = new ArrayList();
					lineColorList.add(ColorUtil.getHexColor(chart.getForegrounds().get(size)));
					nc.setLineColor(lineColorList);
				}
			}
	    	for (int i=0; i < size; i++) {
	    		colors.add(ColorUtil.getHexColor(chart.getForegrounds().get(i)));	    		
	    	}     
    	}
		nc.setColor(colors);
    }
   
    private void setType() {
        byte type = chart.getType().getType();        
        if ((ChartType.BAR == type) || (ChartType.BAR_COMBO == type)) {
            nc.setType(NextChart.Type.bar);           	
        } else if (ChartType.HORIZONTAL_BAR == type) {
        	nc.setType(NextChart.Type.hbar);
        } else if ((ChartType.STACKED_BAR == type) || (ChartType.STACKED_BAR_COMBO == type)) {
        	nc.setType(NextChart.Type.stackedbar);
        } else if (ChartType.HORIZONTAL_STACKED_BAR == type) {
        	nc.setType(NextChart.Type.hstackedbar);	
        } else if (ChartType.PIE == type) {
        	nc.setType(NextChart.Type.pie);
        } else if (ChartType.LINE == type) {
            nc.setType(NextChart.Type.line);
        } else if (ChartType.AREA == type) {        	
        	nc.setType(NextChart.Type.area);
        } else if (ChartType.BUBBLE == type) {        	
        	nc.setType(NextChart.Type.bubble);
        }                        
    }   
    
	private void setStyle() {
		byte style = chart.getType().getStyle();
		switch (style) {
		case ChartType.STYLE_BAR_GLASS:
			nc.setStyle(NextChart.Style.glass);
			break;
		case ChartType.STYLE_BAR_CYLINDER:
			nc.setStyle(NextChart.Style.cylinder);
			break;
		case ChartType.STYLE_BAR_PARALLELIPIPED:
			nc.setStyle(NextChart.Style.parallelepiped);
			break;
		case ChartType.STYLE_BAR_DOME:
			nc.setStyle(NextChart.Style.dome);
			break;
		case ChartType.STYLE_LINE_DOT_SOLID:
			nc.setStyle(NextChart.Style.soliddot);
			break;
		case ChartType.STYLE_LINE_DOT_HOLLOW:
			nc.setStyle(NextChart.Style.hollowdot);
			break;
		case ChartType.STYLE_LINE_DOT_ANCHOR:
			nc.setStyle(NextChart.Style.anchordot);
			break;
		case ChartType.STYLE_LINE_DOT_BOW:
			nc.setStyle(NextChart.Style.bowdot);
			break;	
		case ChartType.STYLE_LINE_DOT_STAR:
			nc.setStyle(NextChart.Style.stardot);
			break;
		default:
			nc.setStyle(NextChart.Style.normal);
			break;
		}
	}
	
	private void setLegends(boolean isCombo) {		
		if ((chart.getYColumnsLegends() != null) && (chart.getYColumnsLegends().size() > 0) && 
			!isEmpty(chart.getYColumnsLegends().get(0))) {			
			List list = new ArrayList();
			int size = chart.getYColumnsLegends().size();
			if (chart.getYColumnsLegends().size() > chart.getYColumns().size()) {
				size = chart.getYColumns().size();
			}
			if (isCombo) {				
				if (size > 1) {
					size--;
					List lineList = new ArrayList();
					lineList.add(StringUtil.getI18nString(replaceParameters(chart.getYColumnsLegends().get(size)),I18nUtil.getLanguageByName(chart, language)));					
					nc.setLineLegend(lineList);
				}
			}
			for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy