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

org.ajax4jsf.framework.renderer.AjaxRendererUtils Maven / Gradle / Ivy

/**
 * Licensed under the Common Development and Distribution License,
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.sun.com/cddl/
 *   
 * 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 org.ajax4jsf.framework.renderer;

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.application.ViewHandler;
import javax.faces.component.ActionSource;
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.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletResponse;

import org.ajax4jsf.ajax.UILoadBundle;
import org.ajax4jsf.framework.ajax.AjaxComponent;
import org.ajax4jsf.framework.ajax.AjaxContainer;
import org.ajax4jsf.framework.ajax.AjaxContext;
import org.ajax4jsf.framework.ajax.AjaxRegionListener;
import org.ajax4jsf.framework.ajax.AjaxSupport;
import org.ajax4jsf.framework.ajax.AjaxViewRoot;
import org.ajax4jsf.framework.ajax.JavaScriptParameter;
import org.ajax4jsf.framework.renderer.RendererUtils.HTML;
import org.ajax4jsf.framework.util.javascript.JSFunction;
import org.ajax4jsf.framework.util.javascript.JSFunctionDefinition;
import org.ajax4jsf.framework.util.javascript.JSReference;
import org.ajax4jsf.framework.util.message.Messages;
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 funtion name for call after complete
	 * request.
	 */
	public static final String ONCOMPLETE_ATTR_NAME = "oncomplete";

	/**
	 * 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";

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

	public static final String SEQUENCE_PARAM = "jsf_sequence";

	private static final String VALUE_ATTR = "value";

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

	private static final String AJAX_SINGLE_ATTR = "ajaxSingle";

	public static final String AJAX_QUEUE_ATTR = "eventsQueue";

	public static final String AJAX_DELAY_ATTR = "requestDelay";

	private static final Object AJAX_ABORT_ATTR = "ignoreDupResponses";

	/**
	 * Static class - protect constructor TODO - make as subclass of chameleon
	 * RendererUtils.
	 * 
	 */
	private AjaxRendererUtils() {

	}

	/**
	 * Build JavaScript onclick event for given component
	 * 
	 * @param uiComponent -
	 *            component for build event
	 * @param facesContext
	 * @return StringBuffer 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 StringBuffer with Javascript code TODO - for
	 *         key-based events build list of supported keys
	 */
	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.
		// TODO
		// sync true/false - run script in sync mode.
		// ..........
		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;

	}

	/**
	 * @param facesContext
	 * @param uiComponent
	 * @return
	 */
	public static Map buildEventOptions(FacesContext facesContext,
			UIComponent uiComponent) {
		String clientId = uiComponent.getClientId(facesContext);
		Map componentAttributes = uiComponent.getAttributes();
		Map options = new HashMap();
		Map parameters = new HashMap();
//		UIForm form = getNestingForm(uiComponent);
		// "input" - if assigned to html input element.
		boolean input = (uiComponent instanceof AjaxSupport && uiComponent
				.getParent() instanceof EditableValueHolder)
				|| uiComponent instanceof EditableValueHolder;
		// Action component - button etc.
		boolean action = (uiComponent instanceof AjaxSupport && uiComponent
				.getParent() instanceof ActionSource)
				|| uiComponent instanceof ActionSource;
		// TODO - disable ( or not ? ) submission of action for disabled default
		// ?
		// if ( action && uiComponent instanceof AjaxSupport) {
		// AjaxSupport support = (AjaxSupport) uiComponent;
		// if (support.isDisableDefault()) {
		// action = false;
		// }
		// }

		boolean ajaxSingle = Boolean.TRUE.equals(componentAttributes
				.get(AJAX_SINGLE_ATTR));
		// For input components in single mode or without form submit input
		// control )
		if ( ajaxSingle && input) { // || action
			// TODO - inside form, put reference :
			// document.forms['form'].controls['control']
			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);
		if (ajaxSingle) {
			options.put("single", JSReference.TRUE);
		}
		// Setup action URL. For portlet environment, it will be different from page.
		options.put("actionUrl", getActionUrl(facesContext));
		// add child parameters
		for (Iterator it = uiComponent.getChildren().iterator(); it.hasNext();) {
			UIComponent child = (UIComponent) 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) {
					parameters.put(name, value);
				} else {
					parameters.put(name, new JSReference(value.toString()));
					// if(it.hasNext()){onEvent.append(',');};
					// renderAjaxLinkParameter( name,
					// value, onClick, jsForm, nestingForm);
				}
			}
		}
		if (!parameters.isEmpty()) {
			options.put("parameters", parameters);
		}
		// parameter to render only current list of areas.
		if (isAjaxLimitToList(uiComponent)) {
			Set ajaxAreas = getAjaxAreas(uiComponent);
			Set areasIds = new HashSet();
			if (null != ajaxAreas) {
				for (Iterator iter = ajaxAreas.iterator(); iter.hasNext();) {
					String id = (String) iter.next();
					UIComponent comp = uiComponent.findComponent(id);
					if (null != comp) {
						areasIds.add(comp.getClientId(facesContext));
					} else {
						areasIds.add(id);
					}
				}
			}
			options.put("affected", areasIds);
		}
		String oncomplete = getAjaxOncomplete(uiComponent);
		if (null != oncomplete) {
			JSFunctionDefinition function = new JSFunctionDefinition("request");
			function.addParameter("event");
			function.addParameter("data");
			function.addToBody(oncomplete);
			
			options.put("oncomplete", function);
		}
		String status = getAjaxStatus(uiComponent);
		if (null != status) {
			options.put("status", status);
		}
		String queue = (String) componentAttributes.get(AJAX_QUEUE_ATTR);
		Integer requestDelay = (Integer) componentAttributes
				.get(AJAX_DELAY_ATTR);
		if (null != requestDelay && requestDelay.intValue() > 0) {
			options.put(AJAX_DELAY_ATTR, requestDelay);
			if (null == queue) {
				queue = clientId;
			}
		}
		Boolean ignoreDupResponses = (Boolean)componentAttributes.get(AJAX_ABORT_ATTR);
		if(null != ignoreDupResponses && ignoreDupResponses.booleanValue()){
			options.put(AJAX_ABORT_ATTR, JSReference.TRUE);
			if (null == queue) {
				queue = clientId;
			}
		}
		if (null != queue) {
			options.put(AJAX_QUEUE_ATTR, queue);
		}
		// 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
	 *            TODO
	 * @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
	 *            TODO
	 * @return
	 */
	public static JSFunction buildAjaxFunction(UIComponent uiComponent,
			FacesContext facesContext, String functionName) {
		JSFunction ajaxFunction = new JSFunction(functionName);
		UIComponent nestingContainer = (UIComponent) findAjaxContainer(
				facesContext, uiComponent);
		ajaxFunction.addParameter(nestingContainer.getClientId(facesContext));
//		 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 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);
	}

	/**
	 * Convert parameter ( Collection, List, array, String, comma-separated
	 * String ) to list of srings. TODO - when move to JDK 5, change to
	 * List<String>
	 * 
	 * @param valueToSet -
	 *            obect for convert to List.
	 * @return - list of strings.
	 */
	public static Set asSet(Object valueToSet) {

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

			}
		}
		return null;
	}

	/**
	 * Get status area Id for given component.
	 * 
	 * @param component
	 * @return clientId of status area, or null
	 */
	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 = null;
	        UIComponent currentParent = component;
	        try {
	            // Check the naming container of the current 
	            // component for component identified by
	            // 'forComponent'
	            while (currentParent != null) {
	                // If the current component is a NamingContainer,
	                // see if it contains what we're looking for.
	                status = currentParent.findComponent(statusId);
	                if (status != null)
	                    break;
	                // if not, start checking further up in the view
	                currentParent = currentParent.getParent();
	            }                   
	        } catch (Exception e) {
	           // ignore - log the warning
	        }
			if (null != status) {
				statusId = status
						.getClientId(FacesContext.getCurrentInstance());
			} else {
				log.warn(Messages.getMessage(
						Messages.AJAX_STATUS_COMPONENT_NOT_FOWND_WARNING,
						component.getId()));
			}
		}
		return statusId;
	}

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

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

	/**
	 * Calculate, must be component render only given areas, or all sended from
	 * server.
	 * 
	 * @param component
	 * @return true 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 RendererUtils
	 * 
	 * @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 UIForm component, or null
	 */
	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 getActionUrl(FacesContext facesContext) {
		ViewHandler viewHandler = facesContext.getApplication()
				.getViewHandler();
		String viewId = facesContext.getViewRoot().getViewId();
		return facesContext.getExternalContext().encodeActionURL(
				viewHandler.getActionURL(facesContext, viewId));
	}

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

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

	public static AjaxContainer getSubmittedAjaxContainer(FacesContext context,
			AjaxContainer container) {
		UIViewRoot root = context.getViewRoot();
		if (root instanceof AjaxViewRoot) {
			AjaxViewRoot ajaxRoot = (AjaxViewRoot) root;
			UIComponent submitted = ajaxRoot.getSubmittedRegion(context);
			if (null == submitted) {
				return ajaxRoot;
			} else {
				return (AjaxContainer) submitted;
			}
		} else {
			return container;
		}
	}

	/**
	 * For Myfaces, get current viev sequence.
	 * 
	 * @param facescontext
	 * @return
	 */
	private static Integer getViewSequence(FacesContext facescontext) {
		Map map = facescontext.getExternalContext().getRequestMap();
		Integer sequence = (Integer) map.get(SEQUENCE_PARAM);
		if (sequence == null) {
			sequence = new Integer(1);
			map.put(SEQUENCE_PARAM, sequence);
		}
		return sequence;
	}

	/**
	 * Encode rendered areas as special HTML tag ( span in current release )
	 * 
	 * @param context
	 * @param component
	 * @throws IOException
	 */
	public static boolean encodeAreas(FacesContext context,
			UIComponent component) throws IOException {
		AjaxContainer ajax = getSubmittedAjaxContainer(context,
				(AjaxContainer) component);
		// Out only for current container
		if (null != ajax) {
			ExternalContext externalContext = context.getExternalContext();
			Map requestMap = externalContext.getRequestMap();
			if (ajax.isSubmitted()
					&& (null == requestMap.get(AJAX_AREAS_RENDERED))) {
				AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
				Set 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  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 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 
				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 responseDataMap = ajaxContext.getResponseDataMap();
				// Put data to JavaScript handlers, inside  elements.
				for (Iterator dataIterator = responseDataMap.keySet().iterator(); dataIterator.hasNext();) {
					Object dataKey =  dataIterator.next();
					out.startElement(HTML.SPAN_ELEM, component);
					out.writeAttribute(HTML.id_ATTRIBUTE, dataKey, null);
					out.write(responseDataMap.get(dataKey).toString());
					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");
				return true;
			}
		}
		return false;
	}


	/**
	 * Write state saving markers to context, include MyFaces view sequence.
	 * 
	 * @param context
	 * @throws IOException
	 */
	public static void writeState(FacesContext context) throws IOException {
		if (!context.getApplication().getStateManager().isSavingStateInClient(
				context)) {
			ResponseWriter writer = context.getResponseWriter();
			// MyFaces-specific view sequence attribute
			writer.startElement(HTML.INPUT_ELEM, null);
			writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
			writer.writeAttribute(HTML.NAME_ATTRIBUTE,
					AjaxRendererUtils.SEQUENCE_PARAM, null);
			writer.writeAttribute(HTML.value_ATTRIBUTE, AjaxRendererUtils
					.getViewSequence(context), null);
			writer.endElement(HTML.INPUT_ELEM);
		}
		context.getApplication().getViewHandler().writeState(context);
	}

	/**
	 * Encode declaration for AJAX response. Render <html><body>
	 * 
	 * @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);
		// TODO - html attributes. lang - from current locale ?
		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
	 * </body></html>
	 * 
	 * @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");

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

	/**
	 * Detect AJAX status for request
	 * 
	 * @param context
	 * @return
	 */
	public static boolean isAjaxRequest(FacesContext context) {
		UIViewRoot root = context.getViewRoot();
		if (root instanceof AjaxViewRoot) {
			return ((AjaxViewRoot) root).isAjaxRequest();
		} else {
			return context.getExternalContext().getRequestParameterMap()
					.containsKey(AjaxContainerRenderer.AJAX_PARAMETER_NAME);
		}
	}

	/**
	 * Detect AJAX status for request
	 * 
	 * @param context
	 * @return
	 */
	public static boolean isAjaxRequest() {
		return isAjaxRequest(FacesContext.getCurrentInstance());
	}

	/**
	 * Add affected regions's ID to ajaxView component.
	 * 
	 * @param component
	 * @param context
	 *            TODO
	 */
	public static void addRegionsFromComponent(UIComponent component,
			FacesContext context) {
		// First step - find parent ajax view
		Object ajaxRegions = getAjaxAreas(component);
		// if (ajaxRegions == null){
		// FacesContext context = FacesContext.getCurrentInstance();
		// ajaxRegions = AjaxRendererUtils.getAbsoluteId(context,component);
		// }
		if (AjaxRegionListener.log.isDebugEnabled()) {
			AjaxRegionListener.log.debug(Messages.getMessage(
					Messages.INVOKE_AJAX_REGION_LISTENER, component.getId()));
		}
		addRegionByName(context, component, ajaxRegions);

	}

	/**
	 * @param context
	 * @param component
	 * @param ajaxRegions
	 */
	public static void addRegionByName(FacesContext context,
			UIComponent component, Object ajaxRegions) {
		// convert ID to absolute, if can be found relative to component ...
		if (ajaxRegions != null) {
			AjaxContainer view = getSubmittedAjaxContainer(context, null);
			if (null != view) {
				if (ajaxRegions instanceof Collection) {
					Set areasToRender = view.getAjaxAreasToRender();
					for (Iterator iter = ((Collection) ajaxRegions).iterator(); iter
							.hasNext();) {
						String id = iter.next().toString();
						areasToRender.add(convertId(component, id));
					}
					// view.addAjaxAreas((Collection) ajaxRegions);
				} else {
					view.addAjaxArea(convertId(component, ajaxRegions
							.toString()));
				}
			} else {
				AjaxRegionListener.log
						.warn(Messages
								.getMessage(Messages.APPENDING_AJAX_REGION_TO_NON_AJAX_CONTAINER_WARNING));
			}
		}
	}

	/**
	 * Test for relative id of target components. Attempt convert to absolute.
	 * For use as argument for
	 * {@link UIComponent#findComponent(java.lang.String)}
	 * 
	 * @param component
	 * @param id
	 * @return
	 */
	public static String convertId(UIComponent component, String id) {
		if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR) {
			return id;
		}
		if(null!=component){
		UIComponent target = component.findComponent(id);
		if (null != target) {
			return getAbsoluteId(target);
		}
		}
		return id;
	}

	/**
	 * 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 iter = component.getChildren().iterator(); iter.hasNext();) {
			UIComponent child = (UIComponent) iter.next();
			loadCildBundles(context, child);
		}
		// Iterate over facets
		for (Iterator 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 UILoadBundle) {
			try {
				child.encodeBegin(context);
			} catch (IOException e) {
				// DO nothing - really, LoadBundle don't can throw exceptions.
			}
		} else {
			loadBundles(context, child);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy