Package org.richfaces.sandbox.chart

Source Code of org.richfaces.sandbox.chart.ChartRendererBase$VisitChart

package org.richfaces.sandbox.chart;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;

import org.richfaces.json.JSONException;
import org.richfaces.json.JSONObject;
import org.richfaces.renderkit.RendererBase;
import org.richfaces.sandbox.chart.component.AbstractChart;
import org.richfaces.sandbox.chart.component.AbstractLegend;
import org.richfaces.sandbox.chart.component.AbstractSeries;
import org.richfaces.sandbox.chart.component.AbstractXaxis;
import org.richfaces.sandbox.chart.component.AbstractYaxis;

import static java.util.Arrays.asList;

import java.util.HashMap;
import java.util.Map;

import javax.faces.FacesException;

import org.richfaces.javascript.JSFunctionDefinition;
import org.richfaces.javascript.JSReference;
import org.richfaces.json.JSONArray;
import org.richfaces.renderkit.RenderKitUtils;

import static org.richfaces.renderkit.RenderKitUtils.addToScriptHash;
import static org.richfaces.renderkit.RenderKitUtils.attributes;

import org.richfaces.sandbox.chart.component.AbstractPoint;
import org.richfaces.sandbox.chart.model.ChartDataModel;
import org.richfaces.sandbox.chart.model.NumberChartDataModel;
import org.richfaces.sandbox.chart.model.StringChartDataModel;
import org.richfaces.sandbox.chart.model.ChartDataModel.ChartType;
import org.richfaces.ui.common.AjaxFunction;
import org.richfaces.util.AjaxRendererUtils;

/**
*
*
* @author Lukas Macko
*/
public abstract class ChartRendererBase extends RendererBase {

    public static final String RENDERER_TYPE = "org.richfaces.sandbox.ChartRenderer";
   
    private static final String X_VALUE = "x";
    private static final String Y_VALUE = "y";
    private static final String POINT_INDEX = "pointIndex";
    private static final String SERIES_INDEX = "seriesIndex";
    private static final String EVENT_TYPE = "eventType";
    private static final String PLOT_CLICK_TYPE = "plotclick";

    /**
     * Method adds key-value pair to object.
     *
     * @param obj
     * @param key
     * @param value
     * @return
     * @throws IOException if put to JSONObject fails
     */
    public static JSONObject addAttribute(JSONObject obj, String key, Object value) throws IOException {
        try {
            if (value != null && !value.equals("")) {
                obj.put(key, value);
            }
        } catch (JSONException ex) {
            throw new IOException("JSONObject put failed.");
        }
        return obj;
    }

    /**
     * Method creates JSON containing chart options
     * @param context
     * @param component
     * @return
     * @throws IOException
     */
    public JSONObject getOpts(FacesContext context, UIComponent component) throws IOException {
        JSONObject obj = new JSONObject();
        addAttribute(obj, "zoom", component.getAttributes().get("zoom"));
        addAttribute(obj, "charttype", component.getAttributes().get("charttype"));
        addAttribute(obj, "xtype", component.getAttributes().get("xtype"));
        addAttribute(obj, "ytype", component.getAttributes().get("ytype"));

        JSONObject xaxis = new JSONObject();
        addAttribute(xaxis, "min", component.getAttributes().get("xmin"));
        addAttribute(xaxis, "max", component.getAttributes().get("xmax"));
        addAttribute(xaxis, "autoscaleMargin", component.getAttributes().get("xpad"));
        addAttribute(xaxis, "axisLabel", component.getAttributes().get("xlabel"));
        addAttribute(xaxis, "format", component.getAttributes().get("xformat"));

        JSONObject yaxis = new JSONObject();
        addAttribute(yaxis, "min", component.getAttributes().get("ymin"));
        addAttribute(yaxis, "max", component.getAttributes().get("ymax"));
        addAttribute(yaxis, "autoscaleMargin", component.getAttributes().get("ypad"));
        addAttribute(yaxis, "axisLabel", component.getAttributes().get("ylabel"));
        addAttribute(yaxis, "format", component.getAttributes().get("yformat"));

        JSONObject legend = new JSONObject();
        addAttribute(legend, "position", component.getAttributes().get("position"));
        addAttribute(legend, "sorted", component.getAttributes().get("sorting"));

        addAttribute(obj, "xaxis", xaxis);
        addAttribute(obj, "yaxis", yaxis);
        addAttribute(obj, "legend", legend);
        return obj;
    }

    @Override
    public void decode(FacesContext context, UIComponent component) {
        super.decode(context, component);
       
        if(!component.isRendered()){
            return;
        }
        Map<String, String> requestParameterMap = context.getExternalContext().getRequestParameterMap();
        if (requestParameterMap.get(component.getClientId(context)) != null) {
            String xParam = requestParameterMap.get(getFieldId(component, X_VALUE));
            String yParam = requestParameterMap.get(getFieldId(component, Y_VALUE));
            String pointIndexParam = requestParameterMap.get(getFieldId(component, POINT_INDEX));
            String eventTypeParam = requestParameterMap.get(getFieldId(component, EVENT_TYPE));
            String seriesIndexParam = requestParameterMap.get(getFieldId(component, SERIES_INDEX));
            try {

                if (PLOT_CLICK_TYPE.equals(eventTypeParam)) {
                    double y = Double.parseDouble(yParam);
                    int seriesIndex = Integer.parseInt(seriesIndexParam);
                    int pointIndex = Integer.parseInt(pointIndexParam);
                    String x = xParam;
                    new PlotClickEvent(component, seriesIndex, pointIndex, x, y).queue();
                }
            } catch (NumberFormatException ex) {
                throw new FacesException("Cannot convert request parmeters", ex);
            }
        }
    }

   
    /**
     * Returns chart chart data
     * @param ctx
     * @param component
     * @return
     */
    public JSONArray getData(FacesContext ctx, UIComponent component) {
        return (JSONArray) component.getAttributes().get("data");
    }
   

    /**
     * Method process chart tags, it collects chart options and data. 
     */
    @Override
    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
        super.encodeBegin(context, component);

        AbstractChart chart = (AbstractChart) component;

        VisitChart visitCallback = new VisitChart(chart);
        //copy attributes to parent tag and process data
        chart.visitTree(VisitContext.createVisitContext(FacesContext.getCurrentInstance()), visitCallback);

        //store data to parent tag
        component.getAttributes().put("data", visitCallback.getData());
       
        if(!visitCallback.isDataEmpty()){
          component.getAttributes().put("charttype", visitCallback.getChartType());
          component.getAttributes().put("xtype", visitCallback.getKeyType());
          component.getAttributes().put("ytype", visitCallback.getValType());
        }
       
        component.getAttributes().put("handlers", visitCallback.getSeriesSpecificHandlers());


    }
   
    public JSONObject getParticularSeriesHandler(FacesContext context,UIComponent component){
        return (JSONObject) component.getAttributes().get("handlers");
    };
   
    public JSFunctionDefinition createEventFunction(FacesContext context, UIComponent component){
        Map<String,Object> params = new HashMap<String, Object>();
        params.put(getFieldId(component, SERIES_INDEX), new JSReference(SERIES_INDEX));
        params.put(getFieldId(component, POINT_INDEX), new JSReference(POINT_INDEX));
        params.put(getFieldId(component, X_VALUE), new JSReference(X_VALUE));
        params.put(getFieldId(component, Y_VALUE), new JSReference(Y_VALUE));
        params.put(getFieldId(component, EVENT_TYPE), new JSReference(EVENT_TYPE));
       
       
        AjaxFunction ajaxFce = AjaxRendererUtils.buildAjaxFunction(context, component);
        ajaxFce.getOptions().getParameters().putAll(params);
       
        return new JSFunctionDefinition("event",EVENT_TYPE,SERIES_INDEX,
                POINT_INDEX,X_VALUE,Y_VALUE).addToBody(ajaxFce);
    }
   
    /**
     * Method creates unique identifier for request parameter.
     *
     * @param component
     * @param attribute
     * @return
     */
    public String getFieldId(UIComponent component, String attribute) {
        return component.getClientId() + "-" + attribute;
    }

    /**
     * Callback loop through children tags: axis, series, legend
     */
    class VisitChart implements VisitCallback {

        private AbstractChart chart;
        private JSONArray data;
        private JSONObject seriesSpecificHandlers;
        private JSONArray plotClickHandlers;
        private JSONArray plothoverHandlers;
        private ChartDataModel.ChartType chartType;
        private Class keyType;
        private Class valType;
        private RenderKitUtils.ScriptHashVariableWrapper eventWrapper=RenderKitUtils.ScriptHashVariableWrapper.eventHandler;
        private boolean nodata;
        public VisitChart(AbstractChart ch) {
            this.nodata=true;
            this.chart = ch;
            this.chartType = null;
            this.data = new JSONArray();
            this.seriesSpecificHandlers = new JSONObject();
            this.plotClickHandlers = new JSONArray();
            this.plothoverHandlers = new JSONArray();
           
            try{
                addAttribute(seriesSpecificHandlers, "onplotclick", plotClickHandlers);
                addAttribute(seriesSpecificHandlers, "onplothover", plothoverHandlers);
            } catch (IOException ex){
                throw new FacesException(ex);
            }
        }

        private void copyAttr(UIComponent src, UIComponent target, String prefix, String attr) {
            Object val = src.getAttributes().get(attr);
            if (val != null) {
                target.getAttributes().put(prefix + attr, val);
            }
        }

        /**
         * Copy attributes from source UIComponent to target.
         *
         * @param src
         * @param target
         * @param prefix
         * @param attrs
         */
        private void copyAttrs(UIComponent src, UIComponent target, String prefix, List<String> attrs) {
            for (Iterator<String> it = attrs.iterator(); it.hasNext();) {
                String attr = it.next();
                copyAttr(src, target, prefix, attr);
            }
        }

        @Override
        public VisitResult visit(VisitContext context, UIComponent target) {
           
           
           
           
           
            if (target instanceof AbstractLegend) {
                copyAttrs(target, chart, "", asList("position", "sorting"));
            } else if (target instanceof AbstractSeries) {
                AbstractSeries s = (AbstractSeries) target;
                ChartDataModel model = s.getData();
               
                //Collect Series specific handlers
                Map<String,Object> optMap = new HashMap<String, Object>();
                RenderKitUtils.Attributes seriesEvents = attributes()
                .generic("onplothover","onplothover","plothover")
              .generic("onplotclick","onplotclick","plotclick");
               
                addToScriptHash(optMap, context.getFacesContext(), target, seriesEvents, RenderKitUtils.ScriptHashVariableWrapper.eventHandler);
               
                if(optMap.get("onplotclick")!=null){
                    plotClickHandlers.put(new RawJSONString(optMap.get("onplotclick").toString()));
                }
                else{
                    plotClickHandlers.put(s.getOnplotclick());
                }
               
               
                if(optMap.get("onplothover")!=null){
                    plothoverHandlers.put(new RawJSONString(optMap.get("onplothover").toString()));
                }
                else{
                    plothoverHandlers.put(s.getOnplothover());
                }
                //end collect series specific handler
               
               
                if (model == null) {
                    /**
                     * data model priority: if there is data model passed
                     * through data attribute use it. Otherwise nested point
                     * tags are expected.
                     */
                    VisitSeries seriesCallback = new VisitSeries(s.getType());
                    s.visitTree(VisitContext.createVisitContext(FacesContext.getCurrentInstance()), seriesCallback);
                    model = seriesCallback.getModel();
                   
                    //if series has no data create empty model
                    if(model==null){
                      switch (s.getType()) {
            case line:
              model = new NumberChartDataModel(ChartType.line);
              break;
            case bar:
              model = new NumberChartDataModel(ChartType.bar);
              break;
            case pie:
              model = new StringChartDataModel(ChartType.pie);
              break;
            default:
              break;
            }
                    }
                    else{
                      nodata=false;
                    }
                }
                else{
                  nodata=false;
                }
                model.setAttributes(s.getAttributes());
               
                try {
                    //Check model/series compatibility
                   
                    if(chartType==null && (!nodata)){
                      //if series is empty do not set types
                        chartType= model.getType();
                        keyType  = model.getKeyType();
                        valType  = model.getValueType();
                    }
                    else{
                        if(chartType== ChartDataModel.ChartType.pie){
                            throw new IllegalArgumentException("Pie chart supports only one series.");
                        }
                    }
                    if(keyType != model.getKeyType() || valType != model.getValueType()){
                        throw new IllegalArgumentException("Data model is not valid for this chart type.");
                    }
                   
                   
                    data.put(model.export());
                } catch (IOException ex) {
                    throw new FacesException(ex);
                }

            } else if (target instanceof AbstractXaxis) {
                copyAttrs(target, chart, "x", asList("min", "max", "pad", "label", "format"));
            } else if (target instanceof AbstractYaxis) {
                copyAttrs(target, chart, "y", asList("min", "max", "pad", "label", "format"));
            }
            return VisitResult.ACCEPT;
        }
        public boolean isDataEmpty(){
          return nodata;
        }
       
        public JSONArray getData() {
            return data;
        }

        public Class getKeyType() {
            return keyType;
        }

        public Class getValType() {
            return valType;
        }
       

        public ChartDataModel.ChartType getChartType() {
            return chartType;
        }

       
        public JSONObject getSeriesSpecificHandlers() {
            return seriesSpecificHandlers;
        }
       
    }

    /**
     * Callback loops through series children tags - points
     */
    class VisitSeries implements VisitCallback {

        private ChartDataModel model=null;
        private ChartDataModel.ChartType type;

        public VisitSeries(ChartDataModel.ChartType type) {
            this.type = type;
        
        }

        @Override
        public VisitResult visit(VisitContext context, UIComponent target) {
           
            if (target instanceof AbstractPoint) {

                AbstractPoint p = (AbstractPoint) target;

                Object x = p.getX();
                Object y = p.getY();

                //the first point determine type of data model
                if (model == null) {
                    if (x instanceof Number && y instanceof Number) {
                        model = new NumberChartDataModel(type);
                    }
                    else if(x instanceof String && y instanceof Number){
                        model = new StringChartDataModel(type);
                    }
                    else {
                        throw new IllegalArgumentException("Not supported type");
                    }
                }


                if (model.getKeyType().isAssignableFrom(x.getClass()) &&
                    model.getValueType().isAssignableFrom(y.getClass())) {
                   
                   
                    if(x instanceof Number && y instanceof Number){
                       model.put((Number)x,(Number)y);
                    }
                    else if(x instanceof String && y instanceof Number){
                       model.put((String) x, (Number)y);
                    }
                    else{
                       throw new IllegalArgumentException("Not supported types " + x.getClass()+" " +y.getClass()+" for "+model.getClass());
                    }
                   
                  
                   
                   
                } else {
                    throw new IllegalArgumentException("Not supported types " + x.getClass()+" " +y.getClass()+" for "+model.getClass());
                }
            }
            return VisitResult.ACCEPT;
        }

        public ChartDataModel getModel() {
            return model;
        }
    }
}
TOP

Related Classes of org.richfaces.sandbox.chart.ChartRendererBase$VisitChart

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.