org.ajax4jsf.renderkit.AjaxRendererUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.ajax4jsf.renderkit.AjaxRendererUtils.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
 */

package org.ajax4jsf.renderkit;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.faces.component.EditableValueHolder;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIForm;
import javax.faces.component.UIParameter;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletResponse;

import org.ajax4jsf.Messages;
import org.ajax4jsf.component.AjaxComponent;
import org.ajax4jsf.component.AjaxContainer;
import org.ajax4jsf.component.AjaxLoadBundleComponent;
import org.ajax4jsf.component.AjaxSupport;
import org.ajax4jsf.component.AjaxViewRoot;
import org.ajax4jsf.component.JavaScriptParameter;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.ajax4jsf.util.ServicesUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author shura
 * 
 * Some utilites for render AJAX components.
 */
public class AjaxRendererUtils {

    /**
     * Name Javasript function for submit AJAX request
     */
    public static final String AJAX_FUNCTION_NAME = "A4J.AJAX.Submit";

    /**
     * Attribute for keep clientId of status component
     */
    public static final String STATUS_ATTR_NAME = "status";

    /**
     * Attribute for keep JavaScript function name for call after complete
     * request.
     */
    public static final String ONCOMPLETE_ATTR_NAME = "oncomplete";

    /**
     * Attribute for keep JavaScript function name for call after complete
     * request.
     */
    public static final String ONCOMPLETE_CONTENT_ID = "org.ajax4jsf.oncomplete";

    /**
     * Attribute for keep JavaScript function name for call before updating
     * DOM tree.
     */
    public static final String ONBEFOREDOMUPDATE_ATTR_NAME = "onbeforedomupdate";

    /**
     * Attribute to keep
     */
    public static final String LIMITTOLIST_ATTR_NAME = "limitToList";

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

    public static final String AJAX_REGIONS_ATTRIBUTE = "reRender";

    /**
     * @since 3.3.0
     */
    public static final String AJAX_PROCESS_ATTRIBUTE = "process";

    private static final Class<?> OBJECT_ARRAY_CLASS = (new Object[0]).getClass();

    public static final String VALUE_ATTR = "value";

    public static final String AJAX_AREAS_RENDERED = "org.ajax4jsf.areas.rendered";

    public static final String AJAX_SINGLE_ATTR = "ajaxSingle";

    public static final String AJAX_QUEUE_ATTR = "eventsQueue";

    public static final String AJAX_DELAY_ATTR = "requestDelay";

    public static final String AJAX_ABORT_ATTR = "ignoreDupResponses";

    public static final String AJAX_SINGLE_PARAMETER_NAME = "ajaxSingle";

    public static final String SIMILARITY_GROUPING_ID_ATTR = "similarityGroupingId";

    /**
     * Static class - protect constructor 
     * 
     */
    private AjaxRendererUtils() {

    }

    /**
     * Build JavaScript onclick event for given component
     * 
     * @param uiComponent -
     *            component for build event
     * @param facesContext
     * @return <code>StringBuffer</code> with Javascript code
     */
    public static StringBuffer buildOnClick(UIComponent uiComponent, FacesContext facesContext) {
        return buildOnEvent(uiComponent, facesContext, HTML.onclick_ATTRIBUTE);
    }

    /**
     * Build JavaScript event for component
     * 
     * @param uiComponent -
     *            component for build event
     * @param facesContext
     * @param eventName -
     *            name of event
     * @return <code>StringBuffer</code> with Javascript code 
     */
    public static StringBuffer buildOnEvent(UIComponent uiComponent, FacesContext facesContext, String eventName) {
        StringBuffer onEvent = new StringBuffer();
        if (null != eventName) {
            String commandOnEvent = (String) uiComponent.getAttributes().get(eventName);
            if (commandOnEvent != null) {
                onEvent.append(commandOnEvent);
                onEvent.append(';');
            }
        }
        JSFunction ajaxFunction = buildAjaxFunction(uiComponent, facesContext);
        // Create formal parameter for non-input elements ???
        // Link Control pseudo-object
        // Options map. Possible options for function call :
        // control - name of form control for submit.
        // name - name for link control \
        // value - value of control. - possible replace by parameters ?
        // single true/false - submit all form or only one control.
        // affected - array of element's ID for update on responce.
        // oncomplete - function for call after complete request.
        // status - id of request status component.
        // parameters - map of parameters name/value for append on request.
        // ..........
        ajaxFunction.addParameter(buildEventOptions(facesContext, uiComponent));

        // appendAjaxSubmitParameters(facesContext, uiComponent, onEvent);
        ajaxFunction.appendScript(onEvent);
        if (uiComponent instanceof AjaxSupport) {
            AjaxSupport support = (AjaxSupport) uiComponent;
            if (support.isDisableDefault()) {
                onEvent.append("; return false;");
            }
        }
        log.debug(Messages.getMessage(Messages.BUILD_ONCLICK_INFO, uiComponent.getId(), onEvent.toString()));
        return onEvent;

    }

    public static Map<String, Object> buildEventOptions(FacesContext facesContext, UIComponent component) {

        return buildEventOptions(facesContext, component, null);
    }

    /**
     * @param facesContext
     * @param uiComponent
     * @return
     */
    public static Map<String, Object> buildEventOptions(FacesContext facesContext, UIComponent uiComponent,
            Map<String, Object> params) {
        String clientId = uiComponent.getClientId(facesContext);
        Map<String, Object> componentAttributes = uiComponent.getAttributes();
        Map<String, Object> options = new HashMap<String, Object>();
        Map<String, Object> parameters = new HashMap<String, Object>();
        UIComponent targetComponent = (uiComponent instanceof AjaxSupport) ? uiComponent.getParent() : uiComponent;
        // UIForm form = getNestingForm(uiComponent);
        // "input" - if assigned to html input element.
        boolean input = targetComponent instanceof EditableValueHolder;
        // Action component - button etc.
        //      boolean action = targetComponent instanceof ActionSource;

        boolean ajaxSingle = Boolean.TRUE.equals(componentAttributes.get(AJAX_SINGLE_ATTR));
        // For input components in single mode or without form submit input
        // control )
        if (ajaxSingle) {
            parameters.put(AJAX_SINGLE_PARAMETER_NAME, targetComponent.getClientId(facesContext));
            // options.put("single", JSReference.TRUE);
            if (input) {
                options.put("control", JSReference.THIS);
            }
        }
        // Control value for submit
        String controlName;
        Object controlValue;
        // TODO - make compatible with JSF RI/MyFaces ? use submittedValue ( if
        // any ) for UIInput, converted value for ValueHolder.
        controlName = clientId;
        controlValue = clientId;
        parameters.put(controlName, controlValue);
        AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext);
        // Setup action URL. For portlet environment, it will be different from
        // page. 
        options.put("actionUrl", ajaxContext.getAjaxActionURL(facesContext));
        // Add application-wide Ajax parameters
        parameters.putAll(ajaxContext.getCommonAjaxParameters());
        // add child parameters
        for (Iterator<UIComponent> it = uiComponent.getChildren().iterator(); it.hasNext();) {
            UIComponent child = it.next();
            if (child instanceof UIParameter) {
                String name = ((UIParameter) child).getName();
                Object value = ((UIParameter) child).getValue();
                if (null == name) {
                    throw new IllegalArgumentException(Messages.getMessage(Messages.UNNAMED_PARAMETER_ERROR,
                            uiComponent.getClientId(facesContext)));
                }
                boolean escape = true;
                if (child instanceof JavaScriptParameter) {
                    JavaScriptParameter actionParam = (JavaScriptParameter) child;
                    escape = !actionParam.isNoEscape();
                }
                if (escape) {
                    if (value == null) {
                        value = "";
                    }
                    parameters.put(name, value);
                } else {
                    parameters.put(name, new JSReference(value.toString()));
                    // if(it.hasNext()){onEvent.append(',');};
                    // renderAjaxLinkParameter( name,
                    // value, onClick, jsForm, nestingForm);
                }
            }
        }

        if (params != null) {
            parameters.putAll(params);
        }

        if (!parameters.isEmpty()) {
            options.put("parameters", parameters);
        }
        // parameter to render only current list of areas.
        //      if (isAjaxLimitToList(uiComponent)) {
        //         Set<? extends Object> ajaxAreas = getAjaxAreas(uiComponent);
        //         Set<String> areasIds = new HashSet<String>();
        //         if (null != ajaxAreas) {
        //            for (Iterator<? extends Object> iter = ajaxAreas.iterator(); iter.hasNext();) {
        //               String id = (String) iter.next();
        //               UIComponent comp = RendererUtils.getInstance().
        //                  findComponentFor(uiComponent, id);
        //               if (null != comp) {
        //                  areasIds.add(comp.getClientId(facesContext));
        //               } else {
        //                  areasIds.add(id);
        //               }
        //            }
        //         }
        //         options.put("affected", areasIds);
        //      }
        String oncomplete = getAjaxOncomplete(uiComponent);
        if (null != oncomplete) {
            options.put(ONCOMPLETE_ATTR_NAME, buildAjaxOncomplete(oncomplete));
        }

        String beforeupdate = getAjaxOnBeforeDomUpdate(uiComponent);
        if (null != beforeupdate) {
            options.put(ONBEFOREDOMUPDATE_ATTR_NAME, buildAjaxOnBeforeDomUpdate(beforeupdate));
        }

        String status = getAjaxStatus(uiComponent);
        if (null != status) {
            options.put("status", status);
        }
        String queue = (String) componentAttributes.get(AJAX_QUEUE_ATTR);
        String implicitQueue = null;

        Integer requestDelay = (Integer) componentAttributes.get(AJAX_DELAY_ATTR);
        if (null != requestDelay && requestDelay.intValue() > 0) {
            options.put(AJAX_DELAY_ATTR, requestDelay);
            if (null == queue) {
                implicitQueue = clientId;
            }
        }
        Boolean ignoreDupResponses = (Boolean) componentAttributes.get(AJAX_ABORT_ATTR);
        if (null != ignoreDupResponses && ignoreDupResponses.booleanValue()) {
            options.put(AJAX_ABORT_ATTR, JSReference.TRUE);
            if (null == queue) {
                implicitQueue = clientId;
            }
        }

        if (null != queue) {
            options.put(AJAX_QUEUE_ATTR, queue);
        } else if (implicitQueue != null) {
            options.put("implicitEventsQueue", clientId);
        }

        ExternalContext externalContext = facesContext.getExternalContext();
        String namespace = externalContext.encodeNamespace("");
        if (namespace != null && namespace.length() != 0) {
            options.put("namespace", namespace);
        }

        String similarityGroupingId = (String) componentAttributes.get(SIMILARITY_GROUPING_ID_ATTR);
        if (similarityGroupingId == null || similarityGroupingId.length() == 0) {
            similarityGroupingId = clientId;
        } else {
            similarityGroupingId = externalContext.encodeNamespace(similarityGroupingId);
        }

        options.put(SIMILARITY_GROUPING_ID_ATTR, similarityGroupingId);

        // request timeout.
        Integer timeout = (Integer) componentAttributes.get("timeout");
        if (null != timeout && timeout.intValue() > 0) {
            options.put("timeout", timeout);
        }
        // Encoding for requests
        String encoding = (String) componentAttributes.get("encoding");
        if (null != encoding) {
            options.put("encoding", encoding);
        }
        return options;
    }

    /**
     * Create call to Ajax Submit function with first two parameters
     * 
     * @param uiComponent
     * @param facesContext
     * @param functionName
     * @return
     */
    public static JSFunction buildAjaxFunction(UIComponent uiComponent, FacesContext facesContext) {
        JSFunction ajaxFunction = buildAjaxFunction(uiComponent, facesContext, AJAX_FUNCTION_NAME);
        // client-side script must have reference to event-enabled object.
        ajaxFunction.addParameter(new JSReference("event"));
        return ajaxFunction;
    }

    /**
     * Create call to Ajax Submit function with first two parameters
     * 
     * @param uiComponent
     * @param facesContext
     * @param functionName
     * @return
     */
    public static JSFunction buildAjaxFunction(UIComponent uiComponent, FacesContext facesContext,
            String functionName) {
        JSFunction ajaxFunction = new JSFunction(functionName);
        UIComponent nestingContainer = (UIComponent) findAjaxContainer(facesContext, uiComponent);

        String clientId = nestingContainer.getClientId(facesContext);
        if (clientId != null) {
            ajaxFunction.addParameter(clientId);
        } else {
            // fix for myfaces 1.2.4
            ajaxFunction.addParameter(JSReference.NULL);
        }

        // build form name or ActionUrl for script
        UIComponent nestingForm = getNestingForm(uiComponent);
        if (null == nestingForm) {
            ajaxFunction.addParameter(JSReference.NULL);
        } else {
            ajaxFunction.addParameter(nestingForm.getClientId(facesContext));
        }
        return ajaxFunction;
    }

    /**
     * Append common parameters ( array of affected areas, status area id, on
     * complete function ) to JavaScript event string.
     * 
     * @param uiComponent
     * @param onClick -
     *            buffer with JavaScript code eg... AJAX.Submit(form,this
     */
    // public static void appendAjaxSubmitParameters(FacesContext facesContext,
    // UIComponent uiComponent, StringBuffer onClick)
    // {
    // Set ajaxAreas = getAjaxAreas(uiComponent);
    // onClick.append(',');
    // // parameter to render only current list of areas.
    // if (isAjaxLimitToList(uiComponent) && ajaxAreas != null &&
    // ajaxAreas.size() > 0)
    // {
    // onClick.append('[');
    // Iterator areas = ajaxAreas.iterator();
    // boolean first = true;
    // while (areas.hasNext())
    // {
    // String element = (String) areas.next();
    // UIComponent component = uiComponent.findComponent(element);
    // if (null != component)
    // {
    // if (!first)
    // {
    // onClick.append(',');
    // }
    // else
    // {
    // first = false;
    // }
    // onClick.append('\'');
    // onClick.append(component.getClientId(facesContext));
    // onClick.append('\'');
    // }
    // }
    // onClick.append("]");
    // }
    // else
    // {
    // onClick.append("null");
    // }
    // // insert id of request status element.
    // onClick.append(',');
    // String status = getAjaxStatus(uiComponent);
    // if (null != status)
    // {
    // onClick.append('\'').append(status).append('\'');
    // }
    // else
    // {
    // onClick.append("null");
    // }
    // // insert function name for call after completed request
    // onClick.append(',');
    // String oncomplete = getAjaxOncomplete(uiComponent);
    // if (null != oncomplete)
    // {
    // onClick.append(oncomplete);
    // }
    // else
    // {
    // onClick.append("null");
    // }
    //
    // }
    /**
     * Get list of clientId's for given component
     * 
     * @param uiComponent
     * @return List of areas Id's , updated by this component.
     */
    public static Set<String> getAjaxAreas(UIComponent uiComponent) {
        Object areas;
        if (uiComponent instanceof AjaxComponent) {
            areas = ((AjaxComponent) uiComponent).getReRender();

        } else {
            areas = uiComponent.getAttributes().get(AjaxRendererUtils.AJAX_REGIONS_ATTRIBUTE);
        }
        return asSet(areas);
    }

    /**
     * Returns set of areas to be processed as a result of this component action invocation
     * 
     * @param component
     * @return set of IDs that should be processed as a 
     * @since 3.3.0
     */
    public static Set<String> getAjaxAreasToProcess(UIComponent component) {
        Object areas;

        if (component instanceof AjaxComponent) {
            areas = ((AjaxComponent) component).getProcess();
        } else {
            areas = component.getAttributes().get(AjaxRendererUtils.AJAX_PROCESS_ATTRIBUTE);
        }

        return asSet(areas);
    }

    /**
     * Convert parameter ( Collection, List, array, String, comma-separated
     * String ) to list of srings. 
     * 
     * @param valueToSet -
     *            obect for convert to List.
     * @return - list of strings.
     */
    @SuppressWarnings("unchecked")
    public static Set<String> asSet(Object valueToSet) {

        if (null != valueToSet) {
            // Simplest case - set.
            if (valueToSet instanceof Set) {
                return (Set<String>) valueToSet;
            }
            // Other collections.
            else if (valueToSet instanceof Collection) {
                return new HashSet<String>((Collection<String>) valueToSet);
            }
            // Array
            else if (OBJECT_ARRAY_CLASS.isAssignableFrom(valueToSet.getClass())) {
                return new HashSet<String>(Arrays.asList((String[]) valueToSet));
            }
            // Tokenize string.
            else if (valueToSet instanceof String) {
                String areasString = (String) valueToSet;
                if (areasString.indexOf(",") > 0) {
                    return new HashSet<String>(Arrays.asList(areasString.trim().split("(\\s)*,(\\s)*")));
                } else {
                    Set<String> areasSet = new HashSet<String>(5);
                    areasSet.add(areasString.trim());
                    return areasSet;
                }

            }
        }
        return null;
    }

    /**
     * Get status area Id for given component.
     * 
     * @param component
     * @return clientId of status area, or <code>null</code>
     */
    public static String getAjaxStatus(UIComponent component) {
        String statusId;
        if (component instanceof AjaxComponent) {
            statusId = ((AjaxComponent) component).getStatus();

        } else {
            statusId = (String) component.getAttributes().get(STATUS_ATTR_NAME);
        }
        if (null != statusId) {
            UIComponent status = RendererUtils.getInstance().findComponentFor(component, statusId);

            if (null != status) {
                statusId = status.getClientId(FacesContext.getCurrentInstance());
            } else {
                log.warn(Messages.getMessage(Messages.AJAX_STATUS_COMPONENT_NOT_FOWND_WARNING, component.getId()));
            }
        }
        return statusId;
    }

    public static JSFunctionDefinition buildAjaxOncomplete(String body) {
        JSFunctionDefinition function = new JSFunctionDefinition("request", "event", "data");
        function.addToBody(body);

        return function;
    }

    public static JSFunctionDefinition buildAjaxOnBeforeDomUpdate(String body) {
        JSFunctionDefinition function = new JSFunctionDefinition("request", "event", "data");
        function.addToBody(body);

        return function;
    }

    /**
     * Get function name for call on completed ajax request.
     * 
     * @param component
     *            for wich calculate function name
     * @return name of JavaScript function or <code>null</code>
     */
    public static String getAjaxOncomplete(UIComponent component) {
        if (component instanceof AjaxComponent) {
            return ((AjaxComponent) component).getOncomplete();

        }
        return (String) component.getAttributes().get(ONCOMPLETE_ATTR_NAME);
    }

    /**
     * Get function name for call before update DOM.
     * 
     * @param component
     *            for wich calculate function name
     * @return name of JavaScript function or <code>null</code>
     */
    public static String getAjaxOnBeforeDomUpdate(UIComponent component) {
        if (component instanceof AjaxComponent) {
            return ((AjaxComponent) component).getOnbeforedomupdate();

        }
        return (String) component.getAttributes().get(ONBEFOREDOMUPDATE_ATTR_NAME);
    }

    /**
     * Calculate, must be component render only given areas, or all sended from
     * server.
     * 
     * @param component
     * @return <code>true</code> if client must render ONLY given areas.
     */
    public static boolean isAjaxLimitToList(UIComponent component) {
        boolean result = false;
        if (component instanceof AjaxComponent) {
            result = ((AjaxComponent) component).isLimitToList();

        } else {
            try {
                result = ((Boolean) component.getAttributes().get(LIMITTOLIST_ATTR_NAME)).booleanValue();
            } catch (NullPointerException e) {
                // NullPointer - ignore ...
            } catch (ClassCastException e1) {
                // not Boolean - false ...
            }
        }
        return result;
    }

    /**
     * Replacement for buggy in MyFaces <code>RendererUtils</code>
     * 
     * @param component
     * @return
     */
    public static String getAbsoluteId(UIComponent component) {
        if (component == null)
            throw new NullPointerException(Messages.getMessage(Messages.COMPONENT_NULL_ERROR_2));

        StringBuffer idBuf = new StringBuffer();

        idBuf.append(component.getId());

        UIComponent parent = component;

        while ((parent = parent.getParent()) != null) {
            if (parent instanceof NamingContainer) {
                idBuf.insert(0, NamingContainer.SEPARATOR_CHAR);
                idBuf.insert(0, parent.getId());
            }
        }
        idBuf.insert(0, NamingContainer.SEPARATOR_CHAR);
        log.debug(Messages.getMessage(Messages.CALCULATE_COMPONENT_ID_INFO, component.getId(), idBuf.toString()));
        return idBuf.toString();
    }

    /**
     * Find nested form for given component
     * 
     * @param component
     * @return nested <code>UIForm</code> component, or <code>null</code>
     */
    public static UIComponent getNestingForm(UIComponent component) {
        UIComponent parent = component;
        // Search enclosed UIForm or ADF UIXForm component
        while (parent != null && !(parent instanceof UIForm)
                && !("org.apache.myfaces.trinidad.Form".equals(parent.getFamily()))
                && !("oracle.adf.Form".equals(parent.getFamily()))) {
            parent = parent.getParent();
        }

        return parent;
    }

    protected static String getAjaxActionUrl(FacesContext facesContext) {
        return AjaxContext.getCurrentInstance(facesContext).getAjaxActionURL(facesContext);
    }

    /**
     * @param facesContext
     * @param uiComponent
     * @return
     */
    public static org.ajax4jsf.component.AjaxContainer findAjaxContainer(FacesContext facesContext,
            UIComponent uiComponent) {
        UIComponent parent = uiComponent.getParent();
        while (parent != null && !(parent instanceof org.ajax4jsf.component.AjaxContainer)) {
            parent = parent.getParent();
        }

        org.ajax4jsf.component.AjaxContainer nestingContainer = null;
        if (parent != null) {
            // link is nested inside a form
            nestingContainer = (org.ajax4jsf.component.AjaxContainer) parent;
        } else if (facesContext.getViewRoot() instanceof AjaxViewRoot) {
            nestingContainer = (AjaxContainer) facesContext.getViewRoot();
        }
        return nestingContainer;
    }

    /**
     * Encode rendered areas as special HTML tag ( span in current release )
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    public static void encodeAreas(FacesContext context, UIComponent component) throws IOException {
        AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
        ExternalContext externalContext = context.getExternalContext();
        Map<String, Object> requestMap = externalContext.getRequestMap();
        Set<String> rendered = ajaxContext.getAjaxRenderedAreas();
        StringBuffer senderString = new StringBuffer();
        // write special area for list of rendered elements. Client-side
        // Java
        // Script
        // read this structure for update areas of DOM tree.
        ResponseWriter out = context.getResponseWriter();
        // Create <span> element to keep list rendered aread ( in title
        // attribute )
        // More right will create special namespace for such
        // information,
        // but I want to keep simple html ( xhtml ) document - on case
        // I have troubles with microsoft XMLHTTP validations.
        out.startElement(AjaxContainerRenderer.AJAX_RESULT_GROUP_TAG, component);
        out.writeAttribute(HTML.NAME_ATTRIBUTE, AjaxContainerRenderer.AJAX_UPDATE_HEADER, null);
        for (Iterator<String> it = rendered.iterator(); it.hasNext();) {
            String id = (String) it.next();
            // out.startElement(AJAX_RESULT_TAG, component);
            // out.writeText(id,null);
            // out.endElement(AJAX_RESULT_TAG);
            senderString.append(id);
            if (it.hasNext()) {
                senderString.append(',');
            }
        }
        out.writeAttribute(AjaxContainerRenderer.AJAX_RESULT_GROUP_ATTR, senderString, null);
        out.endElement(AjaxContainerRenderer.AJAX_RESULT_GROUP_TAG);
        // For sequences and client-saved states.

        out.startElement(AjaxContainerRenderer.AJAX_VIEW_STATE_TAG, component);
        out.writeAttribute(HTML.id_ATTRIBUTE, AjaxContainerRenderer.AJAX_VIEW_STATE_ID, null);
        writeState(context);
        out.endElement(AjaxContainerRenderer.AJAX_VIEW_STATE_TAG);
        // Write rendered flag to html <meta>
        out.startElement(AjaxContainerRenderer.AJAX_RESULT_GROUP_TAG, component);
        out.writeAttribute(HTML.id_ATTRIBUTE, AjaxContainerRenderer.AJAX_FLAG_HEADER, null);
        out.writeAttribute(HTML.NAME_ATTRIBUTE, AjaxContainerRenderer.AJAX_FLAG_HEADER, null);
        out.writeAttribute(AjaxContainerRenderer.AJAX_RESULT_GROUP_ATTR, "true", null);
        out.endElement(AjaxContainerRenderer.AJAX_RESULT_GROUP_TAG);
        // set response header with list of rendered ID's
        Object response = externalContext.getResponse();
        // Use reflection for send responce headers - we can get
        // different responces classes
        // for different environment ( portal, cocoon etc )
        if (response instanceof HttpServletResponse) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            //         httpResponse.setHeader(AjaxContainerRenderer.AJAX_UPDATE_HEADER,
            //               senderString.toString());
            httpResponse.setHeader(AjaxContainerRenderer.AJAX_FLAG_HEADER, "true");
        } else {
            try {
                Method setHeadergMethod = response.getClass().getMethod("setHeader",
                        new Class[] { String.class, String.class });
                //            setHeadergMethod.invoke(response, new Object[] {
                //                  AjaxContainerRenderer.AJAX_UPDATE_HEADER,
                //                  senderString.toString() });
                setHeadergMethod.invoke(response, new Object[] { AjaxContainerRenderer.AJAX_FLAG_HEADER, "true" });
            } catch (Exception e) {
                log.error(Messages.getMessage(Messages.DETECTING_ENCODING_DISABLED_ERROR));
                log.error(Messages.getMessage(Messages.OBTAIN_RESPONSE_SET_HEADER_ERROR, e));
            }
        }
        Map<String, Object> responseDataMap = ajaxContext.getResponseDataMap();
        // Get data serializer instance
        AJAXDataSerializer serializer = (AJAXDataSerializer) ServicesUtils
                .getServiceInstance(AJAXDataSerializer.SERVICE);
        // Put data to JavaScript handlers, inside <span> elements.
        for (Iterator<String> dataIterator = responseDataMap.keySet().iterator(); dataIterator.hasNext();) {
            Object dataKey = dataIterator.next();
            out.startElement(HTML.SPAN_ELEM, component);
            out.writeAttribute(HTML.id_ATTRIBUTE, dataKey, null);
            String dataString = serializer.asString(responseDataMap.get(dataKey));
            out.write(dataString);
            out.endElement(HTML.SPAN_ELEM);
        }
        // Include active 'oncomplete' function content :
        Object oncomplete = ajaxContext.getOncomplete();
        if (null != oncomplete) {
            out.startElement(HTML.SPAN_ELEM, component);
            out.writeAttribute(HTML.id_ATTRIBUTE, ONCOMPLETE_CONTENT_ID, null);
            out.writeText(oncomplete, null);
            out.endElement(HTML.SPAN_ELEM);
        }
        // For self-rendered case, we use own methods for replace stateKey by
        // real value
        // in XML filter.
        // if(ajaxContext.isSelfRender()){
        // saveViewState(context, out);
        // }
        requestMap.put(AJAX_AREAS_RENDERED, "true");
    }

    /**
     * Write state saving markers to context, include MyFaces view sequence.
     * 
     * @param context
     * @throws IOException
     */
    public static void writeState(FacesContext context) throws IOException {
        context.getApplication().getViewHandler().writeState(context);
    }

    /**
     * Encode declaration for AJAX response. Render &lt;html&gt;&lt;body&gt;
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    public static void encodeAjaxBegin(FacesContext context, UIComponent component) throws IOException {
        // AjaxContainer ajax = (AjaxContainer) component;
        ResponseWriter out = context.getResponseWriter();
        // DebugUtils.traceView("ViewRoot in AJAX Page encode begin");
        out.startElement("html", component);
        Locale locale = context.getViewRoot().getLocale();
        out.writeAttribute(HTML.lang_ATTRIBUTE, locale.toString(), "lang");
        out.startElement("body", component);
    }

    /**
     * End encoding of AJAX response. Render tag with included areas and close
     * &lt;/body&gt;&lt;/html&gt;
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    public static void encodeAjaxEnd(FacesContext context, UIComponent component) throws IOException {
        // AjaxContainer ajax = (AjaxContainer) component;
        ResponseWriter out = context.getResponseWriter();
        // DebugUtils.traceView("ViewRoot in AJAX Page encode begin");

        encodeAreas(context, component);
        out.endElement("body");
        out.endElement("html");
    }

    /**
     * Find all instances of {@link UILoadBundle} in view tree and load bundles
     * to request-scope map.
     * 
     * @param context
     * @throws IOException
     */
    public static void loadBundles(FacesContext context) {
        // TODO - performanse improove - don't seek by all components tree.
        loadBundles(context, context.getViewRoot());

    }

    /**
     * Recursive helper for {@link #loadBundles(FacesContext)}
     * 
     * @param context
     * @param component
     * @throws IOException
     */
    private static void loadBundles(FacesContext context, UIComponent component) {
        // Iterate over cildrens
        for (Iterator<UIComponent> iter = component.getChildren().iterator(); iter.hasNext();) {
            UIComponent child = (UIComponent) iter.next();
            loadCildBundles(context, child);
        }
        // Iterate over facets
        for (Iterator<UIComponent> iter = component.getFacets().values().iterator(); iter.hasNext();) {
            UIComponent child = (UIComponent) iter.next();
            loadCildBundles(context, child);
        }
    }

    /**
     * @param context
     * @param child
     */
    private static void loadCildBundles(FacesContext context, UIComponent child) {
        if (child instanceof AjaxLoadBundleComponent) {
            try {
                child.encodeBegin(context);
            } catch (IOException e) {
                // DO nothing - really, LoadBundle don't can throw exceptions.
            }
        } else {
            loadBundles(context, child);
        }
    }

    /**
     * @param facesContext
     * @return
     */
    public static boolean isAjaxRequest(FacesContext facesContext) {

        return AjaxContext.getCurrentInstance(facesContext).isAjaxRequest();
    }

    /**
     * TODO: add deprecation
     * 
     * @param facesContext
     * @param component
     * @param id
     */
    public static void addRegionByName(FacesContext facesContext, UIComponent component, String id) {

        AjaxContext.getCurrentInstance(facesContext).addComponentToAjaxRender(component, id);
    }

    /**
     * @param facesContext
     * @param component
     * @param id
     */
    public static void addRegionsFromComponent(UIComponent component, FacesContext facesContext) {

        AjaxContext.getCurrentInstance(facesContext).addRegionsFromComponent(component);
    }

}