org.richfaces.renderkit.AbstractProgressBarRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.richfaces.renderkit.AbstractProgressBarRenderer.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

/*
 * AbstractProgressBarRenderer.java      Date created: 20.12.2007
 * Last modified by: $Author$
 * $Revision$   $Date$
 */

package org.richfaces.renderkit;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.faces.FactoryFinder;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.http.HttpServletResponse;

import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSLiteral;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.ajax4jsf.renderkit.ComponentVariables;
import org.ajax4jsf.renderkit.ComponentsVariableResolver;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.ajax4jsf.resource.CountingOutputWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.component.UIProgressBar;

/**
 * Abstract progress bar renderer
 * 
 * @author "Andrey Markavtsov"
 * 
 */
public class AbstractProgressBarRenderer extends TemplateEncoderRendererBase {

    private static final Log log = LogFactory.getLog(AbstractProgressBarRenderer.class);

    /** Ajax function performing polling */
    private static final String AJAX_POLL_FUNCTION = "A4J.AJAX.Poll";

    /*
     * (non-Javadoc)
     * 
     * @see org.ajax4jsf.renderkit.AjaxCommandRendererBase#doDecode(javax.faces.context.FacesContext,
     *      javax.faces.component.UIComponent)
     */
    @Override
    protected void doDecode(FacesContext facesContext, UIComponent uiComponent) {
        new AjaxEvent(uiComponent).queue();
        //uiComponent.queueEvent(new ActionEvent(uiComponent));
    }

    /**
     * Render progress state forced from javascript
     * 
     * @param state
     * @param context
     * @param component
     * @throws IOException
     */
    public void renderForcedState(String state, FacesContext context, UIComponent component) throws IOException {
        if ("initialState".equals(state)) {
            encodeInitialState(context, component, true);
        } else if ("progressState".equals(state)) {
            encodeProgressState(context, component, true);
        } else if ("completeState".equals(state)) {
            encodeCompletedState(context, component, true);
        }
        component.getAttributes().remove(UIProgressBar.FORCE_PERCENT_PARAM);
    }

    /**
     * Gets state forced from javascript
     * 
     * @param component
     * @return
     */
    public String getForcedState(FacesContext context, UIComponent component) {
        String forcedState = null;
        Map<String, String> params = context.getExternalContext().getRequestParameterMap();
        if (params.containsKey(UIProgressBar.FORCE_PERCENT_PARAM)) {
            forcedState = params.get(UIProgressBar.FORCE_PERCENT_PARAM);
        }
        return forcedState;
    }

    /**
     * Renderes label markup
     * 
     * @param context
     * @param component
     * @return
     */
    public StringBuffer getMarkup(FacesContext context, UIComponent component) {
        StringBuffer result = new StringBuffer();
        CountingOutputWriter customWriter = new CountingOutputWriter();
        try {
            if (hasChildren(component)) {

                ResponseWriter writer = context.getResponseWriter();

                String defaultRenderKitId = context.getApplication().getDefaultRenderKitId();
                if (null == defaultRenderKitId) {
                    defaultRenderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
                }
                RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
                        .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
                RenderKit renderKit = renderKitFactory.getRenderKit(context, defaultRenderKitId);

                ResponseWriter responseWriter = renderKit.createResponseWriter(customWriter, null, "UTF-8");
                context.setResponseWriter(responseWriter);
                writeScriptBody(context, component, true);
                result = customWriter.getContent();

                if (writer != null) {
                    context.setResponseWriter(writer);
                } else {
                    HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
                    PrintWriter wr = response.getWriter();
                    if (wr != null) {
                        context.setResponseWriter(renderKit.createResponseWriter(wr, null, "UTF-8"));
                    }
                }
            } else {
                writeScriptBody(customWriter, (String) component.getAttributes().get("label"));
                result = customWriter.getContent();
            }
        } catch (Exception e) {
            log.error("Error occurred during rendering of progress bar label. It switched to empty string", e);
        }

        return result;

    }

    /**
     * Methods encodes AJAX script for polling
     * 
     * @param context -
     *            faces context
     * @param component -
     *            component instance
     * @throws IOException -
     *             IOException
     */
    public void encodePollScript(FacesContext context, UIComponent component) throws IOException {

        ResponseWriter writer = context.getResponseWriter();
        UIProgressBar progressBar = (UIProgressBar) component;
        String clientId = component.getClientId(context);
        StringBuffer pollScript = new StringBuffer("\n");
        StringBuffer script = new StringBuffer("\n");
        if (isAjaxMode(component) && progressBar.isEnabled()) {
            JSFunction function = AjaxRendererUtils.buildAjaxFunction(component, context, AJAX_POLL_FUNCTION);

            function.addParameter(new JSReference("$('" + clientId + "').component.options"));
            function.appendScript(script);

            pollScript.append(script);
        } else {
            pollScript.append(getStopPollScript(clientId));
        }
        pollScript.append(";\n");
        writer.writeText(pollScript.toString(), null);
    }

    /**
     * Encodes script for state rendering in client mode
     * 
     * @param context
     * @param component
     * @param state
     * @throws IOException
     */
    public void encodeRenderStateScript(FacesContext context, UIComponent component, String state)
            throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        StringBuffer script = new StringBuffer("\n");
        script.append("$('" + component.getClientId(context) + "').component.renderState('").append(state)
                .append("');");
        writer.write(script.toString());
    }

    /**
     * Encodes script for label rendering in client
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    public void encodeLabelScript(FacesContext context, UIComponent component) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        StringBuffer script = new StringBuffer("\n");
        script.append("$('" + component.getClientId(context) + "').component.renderLabel(")
                .append(getMarkup(context, component)).append(",null);\n");
        writer.append(script.toString());

    }

    /**
     * Encode initial javascript
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    public void encodeInitialScript(FacesContext context, UIComponent component, String state) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        UIProgressBar progressBar = (UIProgressBar) component;
        ComponentVariables variables = ComponentsVariableResolver.getVariables(this, component);
        StringBuffer script = new StringBuffer();
        String clientId = component.getClientId(context);
        String containerId = ((UIComponent) AjaxRendererUtils.findAjaxContainer(context, component))
                .getClientId(context);
        String mode = (String) component.getAttributes().get("mode");
        UIComponent form = AjaxRendererUtils.getNestingForm(component);
        String formId = "";
        if (form != null) {
            formId = form.getClientId(context);
        } else if ("ajax".equals(mode)) {
            // Ignore form absent. It can be rendered by forcing from any
            // component
            // throw new FaceletException("Progress bar component in ajax mode
            // should be placed inside the form");
        }
        Number minValue = getNumber(component.getAttributes().get("minValue"));
        Number maxValue = getNumber(component.getAttributes().get("maxValue"));
        Number value = (Number) variables.getVariable("value");
        StringBuffer markup = getMarkup(context, component);

        script.append("new ProgressBar('").append(clientId).append("','") // id
                .append(containerId).append("','") // containerId
                .append(formId).append("','") // formId
                .append(mode).append("',") // mode
                .append(minValue).append(",") // min value
                .append(maxValue).append(","); // max value
        script.append(getContext(component)); // context
        script.append(",");
        script.append(markup != null ? new JSLiteral(markup.toString()) : JSReference.NULL); // markup
        script.append(",");
        script.append(ScriptUtils.toScript(buildAjaxOptions(clientId, // options
                progressBar, context)));
        String progressVar = (String) component.getAttributes().get("progressVar");
        if (progressVar != null) {
            script.append(",'");
            script.append(progressVar); // progress var
            script.append("','");
        } else {
            script.append(",null,'");
        }
        script.append(state);
        script.append("',");
        script.append(value.toString());
        script.append(")\n;");
        writer.write(script.toString());
    }

    /**
     * Creates options map for AJAX requests
     * 
     * @param clientId
     * @param progressBar
     * @param context
     * @return
     */
    private Map buildAjaxOptions(String clientId, UIProgressBar progressBar, FacesContext context) {
        Map options = AjaxRendererUtils.buildEventOptions(context, progressBar);
        Integer interval = new Integer(progressBar.getInterval());
        options.put("pollId", clientId);
        options.put("pollinterval", interval);
        JSFunctionDefinition onsubmit = getUtils().getAsEventHandler(context, progressBar, "onsubmit", "");
        if (onsubmit != null) {
            options.put("onsubmit", onsubmit);
        }
        if (progressBar.getAttributes().containsKey("ignoreDupResponses")) {
            options.put("ignoreDupResponses", progressBar.getAttributes().get("ignoreDupResponses"));
        }
        Map parameters = (Map) options.get("parameters");
        parameters.put("percent", "percent");
        parameters.put(clientId, clientId);
        parameters.put("ajaxSingle", clientId);
        // options.put("onbeforedomupdate", getOnComplete(clientId, progressBar,
        // context));
        return options;
    }

    /**
     * Check if component mode is AJAX
     * 
     * @param component
     * @return
     */
    private boolean isAjaxMode(UIComponent component) {
        String mode = (String) component.getAttributes().get("mode");
        return "ajax".equalsIgnoreCase(mode);
    }

    /**
     * Encodes client mode
     * 
     * @param context
     * @param component
     * @param value
     * @param min
     * @param max
     * @throws IOException
     */
    public void encodeClientMode(FacesContext context, UIComponent component, Number value, Number min, Number max)
            throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String clientId = component.getClientId(context);
        String state = (value.doubleValue() <= min.doubleValue()) ? "initialState"
                : ((value.doubleValue() >= max.doubleValue()) ? "completeState" : "progressState");
        writer.startElement(HTML.DIV_ELEM, component);
        writer.writeAttribute(HTML.id_ATTRIBUTE, clientId, null);
        encodeInitialState(context, component, false);
        encodeProgressState(context, component, false);
        encodeCompletedState(context, component, false);
        startScript(writer, component);
        encodeInitialScript(context, component, state);
        encodeLabelScript(context, component);
        encodeRenderStateScript(context, component, state);
        endScript(writer, component);
        writer.endElement(HTML.DIV_ELEM);
    }

    /**
     * Methods encodes start facet of progress bar component
     * 
     * @param context -
     *            faces context
     * @param component -
     *            component instance
     * @throws IOException -
     *             IOException
     */
    public void encodeInitialState(FacesContext context, UIComponent component, boolean isAjaxMode)
            throws IOException {
        String clientId = component.getClientId(context);
        String initialClass = (String) component.getAttributes().get("initialClass");
        String style = (String) component.getAttributes().get("style");
        if (null == style)
            style = "";
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(HTML.DIV_ELEM, component);
        getUtils().writeAttribute(writer, HTML.class_ATTRIBUTE, initialClass);
        getUtils().writeAttribute(writer, "style", style + (!isAjaxMode ? "display: none" : ""));
        writer.writeAttribute(HTML.id_ATTRIBUTE, (isAjaxMode) ? clientId : clientId + ":initialState", null);
        UIComponent initial = component.getFacet("initial");
        if (initial != null) {
            renderChild(context, initial);
        }
        if (isAjaxMode) {
            startScript(writer, component);
            encodeInitialScript(context, component, "initialState");
            encodePollScript(context, component);
            endScript(writer, component);
        }
        writer.endElement(HTML.DIV_ELEM);

    }

    /**
     * Encodes progress state of the component
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    public void encodeProgressState(FacesContext context, UIComponent component, boolean isAjaxMode)
            throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        ComponentVariables variables = ComponentsVariableResolver.getVariables(this, component);
        String clientId = component.getClientId(context);
        String styleClass = (String) variables.getVariable("styleClass");
        boolean isSimple = isSimpleMarkup(component);
        String style = (String) component.getAttributes().get("style");
        if (null == style)
            style = "";
        String shellClass = (!isSimple) ? "rich-progress-bar-shell-dig " : "rich-progress-bar-shell ";
        writer.startElement("div", component);
        getUtils().writeAttribute(writer, "class", "rich-progress-bar-block rich-progress-bar-width " + shellClass
                + (styleClass != null ? styleClass : ""));
        getUtils().writeAttribute(writer, HTML.id_ATTRIBUTE, (isAjaxMode) ? clientId : clientId + ":progressState");
        getUtils().writeAttribute(writer, "style", style + (!isAjaxMode ? "display: none" : ""));
        getUtils().encodePassThruWithExclusions(context, component, "onsubmit");
        encodeProgressBar(context, component, isSimple);
        if (isAjaxMode) {
            startScript(writer, component);
            encodeInitialScript(context, component, "progressState");
            encodeLabelScript(context, component);
            encodePollScript(context, component);
            endScript(writer, component);
        }
        writer.endElement("div");
    }

    /**
     * Encodes html of component
     * 
     * @param context -
     *            faces context
     * @param component -
     *            component instance
     * @throws IOException
     */
    public void encodeProgressBar(FacesContext context, UIComponent component, boolean isSimple)
            throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String clientId = component.getClientId(context);
        ComponentVariables variables = ComponentsVariableResolver.getVariables(this, component);

        Number value = (Number) variables.getVariable("percent");

        String width = String.valueOf(value.intValue());
        String style = (String) component.getAttributes().get("style");
        if (null == style)
            style = "";

        String completeClass = (String) component.getAttributes().get("completeClass");

        String remainClass = (String) component.getAttributes().get("remainClass");

        if (!isSimple) {
            // <div class='remainClass' id='clientId:remain'
            // style='style'></div>
            writer.startElement("div", component);
            getUtils().writeAttribute(writer, "class",
                    "rich-progress-bar-width rich-progress-bar-remained rich-progress-bar-padding " + remainClass);
            getUtils().writeAttribute(writer, "id", clientId + ":remain");
            getUtils().writeAttribute(writer, "style", style);
            writer.endElement("div");

            // <div class='rich-progress-bar-uploaded-dig' id='clientId:upload'
            // style='style'>
            writer.startElement("div", component);
            getUtils().writeAttribute(writer, "class", "rich-progress-bar-uploaded-dig");
            getUtils().writeAttribute(writer, "id", clientId + ":upload");
            getUtils().writeAttribute(writer, "style",
                    (style != null ? style + ";" : "") + " width: " + width + "%;");

            // <div class='completeClass' id='clientId:complete'
            // style='style'></div>
            writer.startElement("div", component);
            getUtils().writeAttribute(writer, "class",
                    "rich-progress-bar-height-dig rich-progress-bar-width rich-progress-bar-completed rich-progress-bar-padding "
                            + completeClass);
            getUtils().writeAttribute(writer, "id", clientId + ":complete");
            getUtils().writeAttribute(writer, "style", style);
            writer.endElement("div");

            // </div>
            writer.endElement("div");
        } else {
            writer.startElement("div", component);
            getUtils().writeAttribute(writer, "class",
                    "rich-progress-bar-height rich-progress-bar-uploaded " + completeClass);
            getUtils().writeAttribute(writer, "id", clientId + ":upload");
            getUtils().writeAttribute(writer, "style",
                    (style != null ? style + ";" : "") + " width: " + width + "%;");
            writer.write(" ");
            writer.endElement("div");
        }

    }

    /**
     * Returns parameters attr
     * 
     * @param component
     * @param renderer
     * @param percent
     * @return
     */
    public String getParameters(UIComponent component) {
        String parameters = (String) component.getAttributes().get("parameters");
        return parameters;
    }

    /**
     * Writes start script element
     * 
     * @param writer
     * @param component
     * @throws IOException
     */
    private void startScript(ResponseWriter writer, UIComponent component) throws IOException {
        writer.startElement(HTML.SPAN_ELEM, component);
        writer.writeAttribute(HTML.style_ATTRIBUTE, "display: none;", null);
        writer.startElement(HTML.SCRIPT_ELEM, component);
        writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
    }

    /**
     * Writes end script element
     * 
     * @param writer
     * @param component
     * @throws IOException
     */
    private void endScript(ResponseWriter writer, UIComponent component) throws IOException {
        writer.endElement(HTML.SCRIPT_ELEM);
        writer.endElement(HTML.SPAN_ELEM);
    }

    /**
     * Returns context for macrosubstitution
     * 
     * @param component
     * @return
     */
    private JSLiteral getContext(UIComponent component) {
        StringBuffer buffer = new StringBuffer();
        String parameters = getParameters(component);
        JSLiteral literal = null;
        if (parameters != null) {
            buffer.append("{").append(parameters).append("}");
            literal = new JSLiteral(buffer.toString());
        } else {
            literal = new JSLiteral("null");
        }
        return literal;
    }

    /**
     * Return true if component has children components
     * 
     * @param component
     * @return
     */
    private boolean hasChildren(UIComponent component) {
        return (component.getChildCount() != 0);
    }

    /**
     * Returns true if markup should rendered as simple 2 divs
     * 
     * @param component
     * @return
     */
    public boolean isSimpleMarkup(UIComponent component) {
        if (hasChildren(component)) {
            return false;
        } else {
            if (component.getAttributes().get("label") != null) {
                return false;
            }
        }
        return true;
    }

    /**
     * Methods encodes finish facet of progress bar component
     * 
     * @param context -
     *            faces context
     * @param component -
     *            component instance
     * @throws IOException -
     *             IOException
     */
    public void encodeCompletedState(FacesContext context, UIComponent component, boolean isAjaxMode)
            throws IOException {
        String clientId = component.getClientId(context);
        String finishClass = (String) component.getAttributes().get("finishClass");
        String style = (String) component.getAttributes().get("style");
        if (null == style)
            style = "";
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(HTML.DIV_ELEM, component);
        writer.writeAttribute(HTML.id_ATTRIBUTE, (isAjaxMode) ? clientId : clientId + ":completeState", null);
        getUtils().writeAttribute(writer, HTML.class_ATTRIBUTE, finishClass);
        getUtils().writeAttribute(writer, "style", style + (!isAjaxMode ? "display: none" : ""));

        UIComponent completed = component.getFacet("complete");
        if (completed != null) {
            renderChild(context, completed);
        }
        if (isAjaxMode) {
            startScript(writer, component);
            encodeInitialScript(context, component, "completeState");
            writer.write(getStopPollScript(clientId).toString());
            endScript(writer, component);
        }
        writer.endElement(HTML.DIV_ELEM);

    }

    /**
     * Converts value attr to number value
     * 
     * @param v -
     *            value attr
     * @return result
     */
    public Number getNumber(Object v) {
        Number result = null;
        if (v != null) {
            try {
                if (v instanceof String) {
                    result = Double.parseDouble((String) v);
                } else {
                    Number n = (Number) v;
                    if (n instanceof BigDecimal || n instanceof Double || n instanceof Float) {
                        result = n.floatValue();
                    } else if (n instanceof Integer || n instanceof Long) {
                        result = n.longValue();
                    }
                }
            } catch (Exception e) {
                // no action
            }
            return result;
        }
        return new Integer(0);
    }

    /**
     * Calculates percent value according to min & max value
     * 
     * @param value
     * @param minValue
     * @param maxValue
     * @return
     */
    public Number calculatePercent(Number value, Number minValue, Number maxValue) {
        if (minValue.doubleValue() < value.doubleValue() && value.doubleValue() < maxValue.doubleValue()) {
            return (Number) ((value.doubleValue() - minValue.doubleValue()) * 100.0
                    / (maxValue.doubleValue() - minValue.doubleValue()));
        } else if (value.doubleValue() <= minValue.doubleValue()) {
            return 0;
        } else if (value.doubleValue() >= maxValue.doubleValue()) {
            return 100;
        }
        return 0;
    }

    /**
     * Returns JS script to stop polling
     * 
     * @param clientId
     * @return
     */
    private StringBuffer getStopPollScript(String clientId) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("A4J.AJAX.StopPoll('").append(clientId).append("');\n");
        return buffer;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ajax4jsf.renderkit.RendererBase#getComponentClass()
     */
    protected Class getComponentClass() {
        // only poll component is allowed.
        return UIProgressBar.class;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.richfaces.renderkit.TemplateEncoderRendererBase#encodeChildren(javax.faces.context.FacesContext,
     *      javax.faces.component.UIComponent)
     */
    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        ; // We should not render children
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ajax4jsf.renderkit.RendererBase#doEncodeChildren(javax.faces.context.ResponseWriter,
     *      javax.faces.context.FacesContext, javax.faces.component.UIComponent)
     */
    @Override
    public void doEncodeChildren(ResponseWriter writer, FacesContext context, UIComponent component)
            throws IOException {
        ; // We should not render children
    }

}