You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ef...@apache.org on 2007/07/15 18:29:59 UTC

svn commit: r556424 - in /myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr: PPRPanelGroup.java PPRPanelGroupRenderer.java PPRPhaseListener.java

Author: efastl
Date: Sun Jul 15 09:29:54 2007
New Revision: 556424

URL: http://svn.apache.org/viewvc?view=rev&rev=556424
Log:
Fix for TOMAHAWK-1052 (render transient components in PPR updates) + long overdue JavaDoc

Modified:
    myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroup.java
    myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroupRenderer.java
    myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPhaseListener.java

Modified: myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroup.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroup.java?view=diff&rev=556424&r1=556423&r2=556424
==============================================================================
--- myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroup.java (original)
+++ myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroup.java Sun Jul 15 09:29:54 2007
@@ -18,145 +18,168 @@
  */
 package org.apache.myfaces.custom.ppr;
 
-import org.apache.myfaces.component.html.ext.HtmlPanelGroup;
-import org.apache.myfaces.shared_tomahawk.util._ComponentUtils;
-
 import javax.faces.context.FacesContext;
 import javax.faces.el.ValueBinding;
 
+import org.apache.myfaces.component.html.ext.HtmlPanelGroup;
+import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
+
 /**
+ * AJAX component which supports updating its children via AJAX calls. These
+ * updates can occur regularly or based on triggering input components.
+ * 
  * @author Ernst Fastl
  */
 public class PPRPanelGroup extends HtmlPanelGroup {
-	public static final String COMPONENT_TYPE = "org.apache.myfaces.PPRPanelGroup";
+    public static final String COMPONENT_TYPE = "org.apache.myfaces.PPRPanelGroup";
 
-	public static final String COMPONENT_FAMILY = "org.apache.myfaces.PPRPanelGroup";
+    public static final String COMPONENT_FAMILY = "org.apache.myfaces.PPRPanelGroup";
 
-	public static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.PPRPanelGroup";
+    public static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.PPRPanelGroup";
 
-	private String _partialTriggers;
+    private String _partialTriggers;
 
-	private Integer _periodicalUpdate;
+    private Integer _periodicalUpdate;
 
     private String _periodicalTriggers;
 
     private String _partialTriggerPattern;
 
-	private String _inlineLoadingMessage;
-
-	private Boolean _showDebugMessages = new Boolean(false);
+    private String _inlineLoadingMessage;
 
-	private Boolean _stateUpdate = new Boolean(true);
+    private Boolean _showDebugMessages = new Boolean(false);
 
-	public PPRPanelGroup() {
-		setRendererType(DEFAULT_RENDERER_TYPE);
-	}
+    private Boolean _stateUpdate = new Boolean(true);
 
-	public String getFamily() {
-		return COMPONENT_FAMILY;
-	}
-
-	public String getPartialTriggers() {
-		if (_partialTriggers != null)
-			return _partialTriggers;
-		ValueBinding vb = getValueBinding("partialTriggers");
-		return vb != null ? _ComponentUtils.getStringValue(getFacesContext(), vb) : null;
-	}
-
-	public void setPartialTriggers(String partialTriggers) {
-		this._partialTriggers = partialTriggers;
-	}
-
-	public Integer getPeriodicalUpdate() {
-		if (_periodicalUpdate != null)
-			return _periodicalUpdate;
-		ValueBinding vb = getValueBinding("periodicalUpdate");
-		return (vb != null) ? (Integer) vb.getValue(getFacesContext()) : null;
-	}
-
-	public void setPeriodicalUpdate(Integer periodicalUpdate) {
-		_periodicalUpdate = periodicalUpdate;
-	}
-
-    public String getPeriodicalTriggers() {
-		if (_periodicalTriggers != null)
-			return _periodicalTriggers;
-		ValueBinding vb = getValueBinding("periodicalTriggers");
-		return (vb != null) ? (String) vb.getValue(getFacesContext()) : null;
-	}
-
-	public void setPeriodicalTriggers(String periodicalTriggers) {
-		_periodicalTriggers = periodicalTriggers;
-	}
-
-    public String getPartialTriggerPattern() {
-		if (_partialTriggerPattern != null)
-			return _partialTriggerPattern;
-		ValueBinding vb = getValueBinding("partialTriggerPattern");
-		return vb != null ? _ComponentUtils.getStringValue(getFacesContext(), vb) : null;
-	}
-
-	public void setPartialTriggerPattern(String partialTriggerPattern) {
-		this._partialTriggerPattern = partialTriggerPattern;
-	}
-
-	public String getInlineLoadingMessage() {
-		if (_inlineLoadingMessage != null)
-			return _inlineLoadingMessage;
-		ValueBinding vb = getValueBinding("inlineLoadingMessage");
-		return vb != null ? _ComponentUtils.getStringValue(getFacesContext(), vb) : null;
-	}
-
-	public void setInlineLoadingMessage(String inlineLoadingMessage) {
-		this._inlineLoadingMessage = inlineLoadingMessage;
-	}
-
-	public Boolean getShowDebugMessages() {
-		if (_showDebugMessages != null) {
-			return _showDebugMessages;
-		}
-		ValueBinding vb = getValueBinding("showDebugMessages");
-		return vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
-	}
-
-	public void setShowDebugMessages(Boolean showDebugMessages) {
-		_showDebugMessages = showDebugMessages;
-	}
-
-	public Boolean getStateUpdate() {
-		if (_stateUpdate != null) {
-			return _stateUpdate;
-		}
-		ValueBinding vb = getValueBinding("stateUpdate");
-		return vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
-	}
-
-	public void setStateUpdate(Boolean stateUpdate) {
-		_stateUpdate = stateUpdate;
-	}
-
-	public void restoreState(FacesContext context, Object state) {
-
-		Object[] values = (Object[]) state;
-		super.restoreState(context, values[0]);
-		_partialTriggers = (String) values[1];
-		_partialTriggerPattern = (String) values[2];
-		_periodicalUpdate = (Integer) values[3];
-        _periodicalTriggers = (String) values[4];
-        _showDebugMessages = (Boolean) values[5];
-		_stateUpdate = (Boolean) values[6];
-
-	}
-
-	public Object saveState(FacesContext context) {
-		Object[] values = new Object[7];
-		values[0] = super.saveState(context);
-		values[1] = _partialTriggers;
-		values[2] = _partialTriggerPattern;
-		values[3] = _periodicalUpdate;
-        values[4] = _periodicalTriggers;
-        values[5] = _showDebugMessages;
-		values[6] = _stateUpdate;
-		return values;
-	}
+    public PPRPanelGroup()
+    {
+	setRendererType(DEFAULT_RENDERER_TYPE);
+    }
+
+    public String getFamily()
+    {
+	return COMPONENT_FAMILY;
+    }
+
+    public String getPartialTriggers()
+    {
+	if (_partialTriggers != null)
+	    return _partialTriggers;
+	ValueBinding vb = getValueBinding("partialTriggers");
+	return vb != null ? RendererUtils.getStringValue(getFacesContext(), vb) : null;
+    }
+
+    public void setPartialTriggers(String partialTriggers)
+    {
+	this._partialTriggers = partialTriggers;
+    }
+
+    public Integer getPeriodicalUpdate()
+    {
+	if (_periodicalUpdate != null)
+	    return _periodicalUpdate;
+	ValueBinding vb = getValueBinding("periodicalUpdate");
+	return (vb != null) ? (Integer) vb.getValue(getFacesContext()) : null;
+    }
+
+    public void setPeriodicalUpdate(Integer periodicalUpdate)
+    {
+	_periodicalUpdate = periodicalUpdate;
+    }
+
+    public String getPeriodicalTriggers()
+    {
+	if (_periodicalTriggers != null)
+	    return _periodicalTriggers;
+	ValueBinding vb = getValueBinding("periodicalTriggers");
+	return (vb != null) ? (String) vb.getValue(getFacesContext()) : null;
+    }
+
+    public void setPeriodicalTriggers(String periodicalTriggers)
+    {
+	_periodicalTriggers = periodicalTriggers;
+    }
+
+    public String getPartialTriggerPattern()
+    {
+	if (_partialTriggerPattern != null)
+	    return _partialTriggerPattern;
+	ValueBinding vb = getValueBinding("partialTriggerPattern");
+	return vb != null ? RendererUtils.getStringValue(getFacesContext(), vb) : null;
+    }
+
+    public void setPartialTriggerPattern(String partialTriggerPattern)
+    {
+	this._partialTriggerPattern = partialTriggerPattern;
+    }
+
+    public String getInlineLoadingMessage()
+    {
+	if (_inlineLoadingMessage != null)
+	    return _inlineLoadingMessage;
+	ValueBinding vb = getValueBinding("inlineLoadingMessage");
+	return vb != null ? RendererUtils.getStringValue(getFacesContext(), vb) : null;
+    }
+
+    public void setInlineLoadingMessage(String inlineLoadingMessage)
+    {
+	this._inlineLoadingMessage = inlineLoadingMessage;
+    }
+
+    public Boolean getShowDebugMessages()
+    {
+	if (_showDebugMessages != null)
+	{
+	    return _showDebugMessages;
+	}
+	ValueBinding vb = getValueBinding("showDebugMessages");
+	return vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
+    }
+
+    public void setShowDebugMessages(Boolean showDebugMessages)
+    {
+	_showDebugMessages = showDebugMessages;
+    }
+
+    public Boolean getStateUpdate()
+    {
+	if (_stateUpdate != null)
+	{
+	    return _stateUpdate;
+	}
+	ValueBinding vb = getValueBinding("stateUpdate");
+	return vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
+    }
+
+    public void setStateUpdate(Boolean stateUpdate)
+    {
+	_stateUpdate = stateUpdate;
+    }
+
+    public void restoreState(FacesContext context, Object state)
+    {
+
+	Object[] values = (Object[]) state;
+	super.restoreState(context, values[0]);
+	_partialTriggers = (String) values[1];
+	_partialTriggerPattern = (String) values[2];
+	_periodicalUpdate = (Integer) values[3];
+	_periodicalTriggers = (String) values[4];
+	_showDebugMessages = (Boolean) values[5];
+	_stateUpdate = (Boolean) values[6];
+
+    }
+
+    public Object saveState(FacesContext context)
+    {
+	Object[] values = new Object[7];
+	values[0] = super.saveState(context);
+	values[1] = _partialTriggers;
+	values[2] = _partialTriggerPattern;
+	values[3] = _periodicalUpdate;
+	values[4] = _periodicalTriggers;
+	values[5] = _showDebugMessages;
+	values[6] = _stateUpdate;
+	return values;
+    }
 }

Modified: myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroupRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroupRenderer.java?view=diff&rev=556424&r1=556423&r2=556424
==============================================================================
--- myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroupRenderer.java (original)
+++ myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPanelGroupRenderer.java Sun Jul 15 09:29:54 2007
@@ -43,248 +43,353 @@
  * @author Ernst Fastl
  */
 public class PPRPanelGroupRenderer extends HtmlGroupRenderer {
-	public static final String PPR_INITIALIZED = "org.apache.myfaces.ppr.INITIALIZED";
+    public static final String PPR_INITIALIZED = "org.apache.myfaces.ppr.INITIALIZED";
 
-	public static final String PPR_RESPONSE = "org.apache.myfaces.ppr.RESPONSE";
+    private static Log log = LogFactory.getLog(PPRPanelGroupRenderer.class);
 
-	private static Log log = LogFactory.getLog(PPRPanelGroupRenderer.class);
-
-	private static final String ADD_PARTIAL_TRIGGER_FUNCTION = "addPartialTrigger";
+    private static final String ADD_PARTIAL_TRIGGER_FUNCTION = "addPartialTrigger";
 
     private static final String ADD_PERIODICAL_TRIGGER_FUNCTION = "addPeriodicalTrigger";
 
     private static final String ADD_PARTIAL_TRIGGER_PATTERN_FUNCTION = "addPartialTriggerPattern";
 
-	private static final String ADD_INLINE_LOADING_MESSAGE_FUNCTION = "addInlineLoadingMessage";
+    private static final String ADD_INLINE_LOADING_MESSAGE_FUNCTION = "addInlineLoadingMessage";
+
+    private static final String PPR_JS_FILE = "ppr.js";
 
-	private static final String PPR_JS_FILE = "ppr.js";
+    private static final String MY_FACES_PPR_INIT_CODE = "new org.apache.myfaces.PPRCtrl";
 
-	private static final String MY_FACES_PPR_INIT_CODE = "new org.apache.myfaces.PPRCtrl";
     private static final String DISABLE_RENDER_CHILDREN = "org.apache.myfaces.PPRPanelGroup.disableRenderChildren";
 
-    public void encodeJavaScript(FacesContext facesContext, PPRPanelGroup pprGroup) throws IOException {
-		
-		final ExternalContext externalContext = facesContext.getExternalContext();
-		
-		final Map requestMap = externalContext.getRequestMap();
-
-        //Do not render the JavaScript if answering to a PPR response
-        if (requestMap.containsKey(PPR_RESPONSE)) {
-			return;
-		}
+    public static final String TRANSIENT_MARKER_ATTRIBUTE = "org.apache.myfaces.PPRPanelGroup.transientComponent";
 
-		FormInfo fi = RendererUtils.findNestingForm(pprGroup, facesContext);
-		if (fi == null) {
-			throw new FacesException("PPRPanelGroup must be embedded in a form.");
-		}
+    /**
+         * Renders the start of a span element. Iterates over all child
+         * components and sets transient components to transient=false. Those
+         * components are marked with the TRANSIENT_MARKER_ATTRIBUTE so the
+         * {@link PPRPhaseListener} can reset them to transient in the next
+         * non-PPR Request
+         * 
+         * @param facesContext
+         *                the current {@link FacesContext}
+         * @param uiComponent
+         *                the {@link PPRPanelGroup} to render
+         */
+    public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
+    {
+	if (uiComponent.getId() == null || uiComponent.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
+	{
+	    throw new IllegalArgumentException("'id' is a required attribute for the PPRPanelGroup");
+	}
 
-		if (!requestMap.containsKey(PPR_INITIALIZED)) {
-			requestMap.put(PPR_INITIALIZED, Boolean.TRUE);
+	// todo: in 1.2, better use a combo of
+	// invokeComponent/RendererUtils.renderChildren() instead
+	uiComponent.getAttributes().put(DISABLE_RENDER_CHILDREN, Boolean.TRUE);
+
+	// Iterate over the transient child components and set transient to
+	// false
+	// This is necessary to have those components available for PPR
+	// responses later on
+	for (Iterator iter = uiComponent.getChildren().iterator(); iter.hasNext();)
+	{
+	    UIComponent child = (UIComponent) iter.next();
+	    if (child.isTransient())
+	    {
+		child.setTransient(false);
+		child.getAttributes().put(TRANSIENT_MARKER_ATTRIBUTE, Boolean.TRUE);
+	    }
+	}
 
-			String javascriptLocation = (String) pprGroup.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
-			AddResource addResource = AddResourceFactory.getInstance(facesContext);
-			DojoUtils.addMainInclude(facesContext, pprGroup, javascriptLocation, new DojoConfig());
-			DojoUtils.addRequire(facesContext, pprGroup, "dojo.io.*");
-			DojoUtils.addRequire(facesContext, pprGroup, "dojo.event.*");
-			addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, PPRPanelGroup.class, PPR_JS_FILE);
-		}
+	super.encodeBegin(facesContext, uiComponent);
+    }
 
-		StringBuffer script = new StringBuffer();
+    /**
+         * todo: in 1.2, better use a combo of
+         * invokeComponent/RendererUtils.renderChildren() instead
+         * 
+         * @param context
+         * @param component
+         * @throws IOException
+         */
+    public void encodeChildren(FacesContext context, UIComponent component) throws IOException
+    {
+	Boolean disableRenderChildren = (Boolean) component.getAttributes().get(DISABLE_RENDER_CHILDREN);
 
-        // all JS is put inside a function passed to dojoOnLoad
-        //this is necessary in order to be able to replace all button onClick handlers
+	if (disableRenderChildren != null && disableRenderChildren.booleanValue() == false)
+	    RendererUtils.renderChildren(context, component);
+    }
 
-        script.append("dojo.addOnLoad( function(){ ");
+    /**
+         * Encodes the end of the span-element and afterwards the inline
+         * JavaScript for the client side initialization of the
+         * {@link PPRPanelGroup}.
+         * 
+         * @param facesContext
+         *                the current {@link FacesContext}
+         * @param uiComponent
+         *                the {@link PPRPanelGroup} to render
+         */
+    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
+    {
+	// Render the span end element
+	super.encodeEnd(facesContext, uiComponent);
+	if (uiComponent instanceof PPRPanelGroup)
+	{
+	    PPRPanelGroup pprGroup = (PPRPanelGroup) uiComponent;
+
+	    final String triggers = pprGroup.getPartialTriggers();
+	    final String triggerPattern = pprGroup.getPartialTriggerPattern();
+
+	    // Check if triggers, a pattern or a periodical update is
+                // defined
+	    if ((triggers != null && triggers.length() > 0) || (triggerPattern != null && triggerPattern.length() > 0)
+		    || pprGroup.getPeriodicalUpdate() != null)
+	    {
+		// encode the initialization inline JavaScript
+		encodeJavaScript(facesContext, pprGroup);
+	    }
+	}
 
+	// todo: in 1.2, better use a combo of
+	// invokeComponent/RendererUtils.renderChildren() instead
+	uiComponent.getAttributes().put(DISABLE_RENDER_CHILDREN, Boolean.FALSE);
+    }
 
-        final String formName = fi.getFormName();
-		
-		String pprCtrlReference = "dojo.byId('" + formName + "').myFacesPPRCtrl";
+    /**
+         * Renders inline JavaScript registering an onLoad function for:
+         * <ul>
+         * <li>Initializing the PPRCtrl for the current Form</li>
+         * <li>Registering partialTriggers</li>
+         * <li>Registering partialTriggerPatterns</li>
+         * <li>Starting periodical updates</li>
+         * <li>Registering inline Loading messages</li>
+         * </ul>
+         * 
+         * @param facesContext
+         *                the current {@link FacesContext}
+         * @param pprGroup
+         *                the currently rendered {@link PPRPanelGroup}
+         * @throws IOException
+         *                 if the underlying Layer throws an {@link IOException}
+         *                 it is passed through
+         */
+    private void encodeJavaScript(FacesContext facesContext, PPRPanelGroup pprGroup) throws IOException
+    {
 
-		if (!requestMap.containsKey(PPR_INITIALIZED + "." + formName)) {
-			requestMap.put(PPR_INITIALIZED + "." + formName, Boolean.TRUE);
+	final ExternalContext externalContext = facesContext.getExternalContext();
 
-			script.append(pprCtrlReference + "=" + MY_FACES_PPR_INIT_CODE + "('" + formName + "',"
-					+ pprGroup.getShowDebugMessages().booleanValue() + "," + pprGroup.getStateUpdate().booleanValue() + ");\n");
+	final Map requestMap = externalContext.getRequestMap();
 
-			if (pprGroup.getPeriodicalUpdate() != null) {
-				script.append(pprCtrlReference + ".registerOnSubmitInterceptor();");
-			}
+	// Do not render the JavaScript if answering to a PPR response
+	if (PPRPhaseListener.isPartialRequest(facesContext))
+	{
+	    return;
+	}
 
-		}
+	FormInfo fi = RendererUtils.findNestingForm(pprGroup, facesContext);
+	if (fi == null)
+	{
+	    throw new FacesException("PPRPanelGroup must be embedded in a form.");
+	}
 
-		String clientId = pprGroup.getClientId(facesContext);
+	//Initialize the client side PPR engine
+	if (!requestMap.containsKey(PPR_INITIALIZED))
+	{
+	    requestMap.put(PPR_INITIALIZED, Boolean.TRUE);
+
+	    String javascriptLocation = (String) pprGroup.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
+	    AddResource addResource = AddResourceFactory.getInstance(facesContext);
+	    DojoUtils.addMainInclude(facesContext, pprGroup, javascriptLocation, new DojoConfig());
+	    DojoUtils.addRequire(facesContext, pprGroup, "dojo.io.*");
+	    DojoUtils.addRequire(facesContext, pprGroup, "dojo.event.*");
+	    addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, PPRPanelGroup.class,
+		    PPR_JS_FILE);
+	}
 
-		if (pprGroup.getPeriodicalUpdate() != null) {
-            String periodicalTriggers = pprGroup.getPeriodicalTriggers();
-            //If no periodicalTriggers are set just start the periodical update
-            if (periodicalTriggers == null || periodicalTriggers.trim().length() <= 0) {
-                script.append(pprCtrlReference + ".startPeriodicalUpdate(" + pprGroup.getPeriodicalUpdate() + ",'" + clientId
-					+ "');");
-            }
-            //Otherwise start it when the trigger happens
-            else {
-                List partialTriggers = (new PartialTriggerParser()).parse(periodicalTriggers);
-                String periodicalTriggerId;
-                String periodicalTriggerClientId;
-                UIComponent periodicalTriggerComponent;
-                for (int i=0; i<partialTriggers.size();i++) {
-                    PartialTriggerParser.PartialTrigger trigger = (PartialTriggerParser.PartialTrigger) partialTriggers.get(i);
-                    periodicalTriggerId = trigger.getPartialTriggerId();
-                    periodicalTriggerComponent = pprGroup.findComponent(periodicalTriggerId);
-                    if (periodicalTriggerComponent == null) {
-                        periodicalTriggerComponent = facesContext.getViewRoot().findComponent(periodicalTriggerId);
-                    }
-
-                    //Component found
-                    if (periodicalTriggerComponent != null) {
-                        periodicalTriggerClientId = periodicalTriggerComponent.getClientId(facesContext);
-                        script.append(pprCtrlReference + "." + ADD_PERIODICAL_TRIGGER_FUNCTION + "('" + periodicalTriggerClientId + "',"+
-                                encodeArray(trigger.getEventHooks())+",'"
-                                + clientId + "', " + pprGroup.getPeriodicalUpdate() + ");");
-
-
-                    //Component missing
-                    } else {
-                        if (log.isDebugEnabled()) {
-                            log.debug("PPRPanelGroupRenderer Component with id " + periodicalTriggerId + " not found!");
-                        }
-                    }
-                }
-            }
-		}
+	StringBuffer script = new StringBuffer();
 
-		String partialTriggerId;
-		String partialTriggerClientId;
-		UIComponent partialTriggerComponent;
-
-		String partialTriggers = pprGroup.getPartialTriggers();
-		
-		String partialTriggerPattern = pprGroup.getPartialTriggerPattern();
-		if (partialTriggerPattern != null && partialTriggerPattern.trim().length() > 0) {
-			script.append(pprCtrlReference + "." + ADD_PARTIAL_TRIGGER_PATTERN_FUNCTION + "('" + partialTriggerPattern
-					+ "','" + clientId + "');");
-		}
+	// all JS is put inside a function passed to dojoOnLoad
+	// this is necessary in order to be able to replace all button onClick
+	// handlers
 
-		String inlineLoadingMessage = pprGroup.getInlineLoadingMessage();
+	script.append("dojo.addOnLoad( function(){ ");
 
-		if (inlineLoadingMessage != null && inlineLoadingMessage.trim().length() > 0) {
-			script.append(pprCtrlReference + "." + ADD_INLINE_LOADING_MESSAGE_FUNCTION + "('" + inlineLoadingMessage + "','"
-					+ clientId + "');");
-		}
+	final String formName = fi.getFormName();
+
+	String pprCtrlReference = "dojo.byId('" + formName + "').myFacesPPRCtrl";
+
+	//Each form containing PPRPanelGroups has its own PPRCtrl
+	if (!requestMap.containsKey(PPR_INITIALIZED + "." + formName))
+	{
+	    requestMap.put(PPR_INITIALIZED + "." + formName, Boolean.TRUE);
+
+	    script.append(pprCtrlReference + "=" + MY_FACES_PPR_INIT_CODE + "('" + formName + "',"
+		    + pprGroup.getShowDebugMessages().booleanValue() + "," + pprGroup.getStateUpdate().booleanValue()
+		    + ");\n");
+
+	    if (pprGroup.getPeriodicalUpdate() != null)
+	    {
+		script.append(pprCtrlReference + ".registerOnSubmitInterceptor();");
+	    }
 
-		if (partialTriggers != null && partialTriggers.trim().length() > 0) {
-            List partialTriggerIds = (new PartialTriggerParser()).parse(partialTriggers);
-            for (int i=0; i<partialTriggerIds.size();i++) {
-                PartialTriggerParser.PartialTrigger trigger = (PartialTriggerParser.PartialTrigger) partialTriggerIds.get(i);
-                partialTriggerId = trigger.getPartialTriggerId();
-				partialTriggerComponent = pprGroup.findComponent(partialTriggerId);
-				if (partialTriggerComponent == null) {
-					partialTriggerComponent = facesContext.getViewRoot().findComponent(partialTriggerId);
-				}
-				if (partialTriggerComponent != null) {
-					partialTriggerClientId = partialTriggerComponent.getClientId(facesContext);
-					script.append(pprCtrlReference + "." + ADD_PARTIAL_TRIGGER_FUNCTION + "('" + partialTriggerClientId + "',"+encodeArray(trigger.getEventHooks())+",'"
-							+ clientId + "');");
-				} else {
-					if (log.isDebugEnabled()) {
-						log.debug("PPRPanelGroupRenderer Component with id " + partialTriggerId + " not found!");
-					}
-				}
+	}
+
+	String clientId = pprGroup.getClientId(facesContext);
+
+	//Handle periodical updates
+	if (pprGroup.getPeriodicalUpdate() != null)
+	{
+	    String periodicalTriggers = pprGroup.getPeriodicalTriggers();
+	    // If no periodicalTriggers are set just start the periodical
+	    // update
+	    if (periodicalTriggers == null || periodicalTriggers.trim().length() <= 0)
+	    {
+		script.append(pprCtrlReference + ".startPeriodicalUpdate(" + pprGroup.getPeriodicalUpdate() + ",'"
+			+ clientId + "');");
+	    }
+	    // Otherwise start it when the trigger happens
+	    else
+	    {
+		List partialTriggers = (new PartialTriggerParser()).parse(periodicalTriggers);
+		String periodicalTriggerId;
+		String periodicalTriggerClientId;
+		UIComponent periodicalTriggerComponent;
+		for (int i = 0; i < partialTriggers.size(); i++)
+		{
+		    PartialTriggerParser.PartialTrigger trigger = (PartialTriggerParser.PartialTrigger) partialTriggers
+			    .get(i);
+		    periodicalTriggerId = trigger.getPartialTriggerId();
+		    periodicalTriggerComponent = pprGroup.findComponent(periodicalTriggerId);
+		    if (periodicalTriggerComponent == null)
+		    {
+			periodicalTriggerComponent = facesContext.getViewRoot().findComponent(periodicalTriggerId);
+		    }
+
+		    // Component found
+		    if (periodicalTriggerComponent != null)
+		    {
+			periodicalTriggerClientId = periodicalTriggerComponent.getClientId(facesContext);
+			script.append(pprCtrlReference + "." + ADD_PERIODICAL_TRIGGER_FUNCTION + "('"
+				+ periodicalTriggerClientId + "'," + encodeArray(trigger.getEventHooks()) + ",'"
+				+ clientId + "', " + pprGroup.getPeriodicalUpdate() + ");");
+
+			// Component missing
+		    } else
+		    {
+			if (log.isDebugEnabled())
+			{
+			    log.debug("PPRPanelGroupRenderer Component with id " + periodicalTriggerId + " not found!");
 			}
+		    }
 		}
+	    }
+	}
 
-        //closing the dojo.addOnLoad call
-        script.append("});");
-        renderInlineScript(facesContext, pprGroup, script.toString());
-    }
-
-    private String encodeArray(List eventHooks) {
-        if(eventHooks==null || eventHooks.size()==0) {
-            return "null";
-        }
-        else{
-            StringBuffer buf = new StringBuffer();
-            buf.append("[");
-
-            for (int i = 0; i < eventHooks.size(); i++) {
-                if(i>0)
-                    buf.append(",");
-                String eventHook = (String) eventHooks.get(i);
-                buf.append("'");
-                buf.append(eventHook);
-                buf.append("'");
-            }
-            buf.append("]");
-
-            return buf.toString();
-        }
-    }
-
-    public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
-		if (uiComponent.getId() == null || uiComponent.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) {
-			throw new IllegalArgumentException("'id' is a required attribute for the PPRPanelGroup");
-		}
+	String partialTriggerId;
+	String partialTriggerClientId;
+	UIComponent partialTriggerComponent;
+
+	String partialTriggers = pprGroup.getPartialTriggers();
+
+	String partialTriggerPattern = pprGroup.getPartialTriggerPattern();
+	
+	//handle partial trigger patterns
+	if (partialTriggerPattern != null && partialTriggerPattern.trim().length() > 0)
+	{
+	    script.append(pprCtrlReference + "." + ADD_PARTIAL_TRIGGER_PATTERN_FUNCTION + "('" + partialTriggerPattern
+		    + "','" + clientId + "');");
+	}
 
-        //todo: in 1.2, better use a combo of invokeComponent/RendererUtils.renderChildren() instead
-        uiComponent.getAttributes().put(DISABLE_RENDER_CHILDREN,Boolean.TRUE);
+	String inlineLoadingMessage = pprGroup.getInlineLoadingMessage();
 
-        super.encodeBegin(facesContext, uiComponent);
+	//handle inline loading messages
+	if (inlineLoadingMessage != null && inlineLoadingMessage.trim().length() > 0)
+	{
+	    script.append(pprCtrlReference + "." + ADD_INLINE_LOADING_MESSAGE_FUNCTION + "('" + inlineLoadingMessage
+		    + "','" + clientId + "');");
 	}
 
-    /** todo: in 1.2, better use a combo of invokeComponent/RendererUtils.renderChildren() instead
-     *
-     * @param context
-     * @param component
-     * @throws IOException
-     */
-    public void encodeChildren(FacesContext context, UIComponent component)
-        throws IOException
-    {
-        Boolean disableRenderChildren = (Boolean) component.getAttributes().get(DISABLE_RENDER_CHILDREN);
-
-        if(disableRenderChildren!=null && disableRenderChildren.booleanValue()==false)
-          RendererUtils.renderChildren(context, component);
-    }
-
-    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
-		super.encodeEnd(facesContext, uiComponent);
-		if (uiComponent instanceof PPRPanelGroup) {
-			PPRPanelGroup pprGroup = (PPRPanelGroup) uiComponent;
-
-			final String triggers = pprGroup.getPartialTriggers();
-			final String triggerPattern = pprGroup.getPartialTriggerPattern();
-			
-			if ((triggers != null && triggers.length() > 0)
-					|| (triggerPattern != null && triggerPattern.length() > 0)
-					|| pprGroup.getPeriodicalUpdate() != null) {
-				encodeJavaScript(facesContext, pprGroup);
-			}
+	//handle partial triggers
+	if (partialTriggers != null && partialTriggers.trim().length() > 0)
+	{
+	    List partialTriggerIds = (new PartialTriggerParser()).parse(partialTriggers);
+	    for (int i = 0; i < partialTriggerIds.size(); i++)
+	    {
+		PartialTriggerParser.PartialTrigger trigger = (PartialTriggerParser.PartialTrigger) partialTriggerIds
+			.get(i);
+		partialTriggerId = trigger.getPartialTriggerId();
+		partialTriggerComponent = pprGroup.findComponent(partialTriggerId);
+		if (partialTriggerComponent == null)
+		{
+		    partialTriggerComponent = facesContext.getViewRoot().findComponent(partialTriggerId);
+		}
+		if (partialTriggerComponent != null)
+		{
+		    partialTriggerClientId = partialTriggerComponent.getClientId(facesContext);
+		    script.append(pprCtrlReference + "." + ADD_PARTIAL_TRIGGER_FUNCTION + "('" + partialTriggerClientId
+			    + "'," + encodeArray(trigger.getEventHooks()) + ",'" + clientId + "');");
+		} else
+		{
+		    if (log.isDebugEnabled())
+		    {
+			log.debug("PPRPanelGroupRenderer Component with id " + partialTriggerId + " not found!");
+		    }
 		}
+	    }
+	}
 
-        //todo: in 1.2, better use a combo of invokeComponent/RendererUtils.renderChildren() instead
-        uiComponent.getAttributes().put(DISABLE_RENDER_CHILDREN,Boolean.FALSE);
+	// closing the dojo.addOnLoad call
+	script.append("});");
+	
+	//Really render the script
+	renderInlineScript(facesContext, pprGroup, script.toString());
     }
 
-	/**
-	 * Helper to write an inline javascript at the exact resource location of the
-	 * call.
-	 * 
-	 * @param facesContext
-	 *          The current faces-context.
-	 * @param component
-	 *          The component for which the script is written.
-	 * @param script
-	 *          The script to be written.
-	 * @throws IOException
-	 *           A forwarded exception from the underlying renderer.
-	 */
-	private static void renderInlineScript(FacesContext facesContext, UIComponent component, String script)
-			throws IOException {
-		ResponseWriter writer = facesContext.getResponseWriter();
-		writer.startElement(HTML.SCRIPT_ELEM, component);
-		writer.writeAttribute(HTML.TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
-		writer.write(script);
-		writer.endElement(HTML.SCRIPT_ELEM);
+    private String encodeArray(List eventHooks)
+    {
+	if (eventHooks == null || eventHooks.size() == 0)
+	{
+	    return "null";
+	} else
+	{
+	    StringBuffer buf = new StringBuffer();
+	    buf.append("[");
+
+	    for (int i = 0; i < eventHooks.size(); i++)
+	    {
+		if (i > 0)
+		    buf.append(",");
+		String eventHook = (String) eventHooks.get(i);
+		buf.append("'");
+		buf.append(eventHook);
+		buf.append("'");
+	    }
+	    buf.append("]");
+
+	    return buf.toString();
 	}
+    }
+
+    /**
+         * Helper to write an inline javascript at the exact resource location
+         * of the call.
+         * 
+         * @param facesContext
+         *                The current faces-context.
+         * @param component
+         *                The component for which the script is written.
+         * @param script
+         *                The script to be written.
+         * @throws IOException
+         *                 A forwarded exception from the underlying renderer.
+         */
+    private static void renderInlineScript(FacesContext facesContext, UIComponent component, String script)
+	    throws IOException
+    {
+	ResponseWriter writer = facesContext.getResponseWriter();
+	writer.startElement(HTML.SCRIPT_ELEM, component);
+	writer.writeAttribute(HTML.TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
+	writer.write(script);
+	writer.endElement(HTML.SCRIPT_ELEM);
+    }
 }

Modified: myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPhaseListener.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPhaseListener.java?view=diff&rev=556424&r1=556423&r2=556424
==============================================================================
--- myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPhaseListener.java (original)
+++ myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/ppr/PPRPhaseListener.java Sun Jul 15 09:29:54 2007
@@ -18,11 +18,11 @@
  */
 package org.apache.myfaces.custom.ppr;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
-import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
-import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
 
 import javax.faces.FacesException;
 import javax.faces.application.StateManager;
@@ -35,143 +35,287 @@
 import javax.faces.event.PhaseListener;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
+import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
 
 /**
+ * Before RenderResponse PhaseListener for processing Ajax requests from
+ * {@link PPRPanelGroup}. It also participates in handling transient components
+ * in PPR Requests
+ * 
  * @author Ernst Fastl
  */
 public class PPRPhaseListener implements PhaseListener {
-	private static Log log = LogFactory.getLog(PPRPhaseListener.class);
+    private static Log log = LogFactory.getLog(PPRPhaseListener.class);
 
-	private static final String PPR_PARAMETER = "org.apache.myfaces.PPRCtrl.ajaxRequest";
+    /**
+         * Request parameter which marks a request as PPR request
+         */
+    private static final String PPR_PARAMETER = "org.apache.myfaces.PPRCtrl.ajaxRequest";
+
+    /**
+         * Request parameter containing a comma separated list of component IDs
+         * of the to be updated components
+         */
+    private static final String TRIGGERED_COMPONENTS_PARAMETER = "org.apache.myfaces.PPRCtrl.triggeredComponents";
 
-	private static final String TRIGGERED_COMPONENTS_PARAMETER = "org.apache.myfaces.PPRCtrl.triggeredComponents";
+    private static final String XML_HEADER = "<?xml version=\"1.0\"?>\n";
 
-	private static final String XML_HEADER = "<?xml version=\"1.0\"?>\n";
+    public void afterPhase(PhaseEvent phaseEvent)
+    {
+    }
 
-	public void afterPhase(PhaseEvent phaseEvent) {
+    /**
+         * Determines wether the currently processed request is a PPR request
+         * (by searching for PPR_PARAMETER in the request parameter map) or an
+         * ordinary HTTP request. If the request is a PPR request the triggered
+         * components are encoded. Otherwise transient components which have
+         * previously been marked not transient by the
+         * {@link PPRPanelGroupRenderer} are set to transient again
+         */
+    public void beforePhase(PhaseEvent event)
+    {
+	if (log.isDebugEnabled())
+	{
+	    log.debug("In PPRPhaseListener beforePhase");
 	}
 
-	public void beforePhase(PhaseEvent event) {
-		if (log.isDebugEnabled()) {
-			log.debug("In PPRPhaseListener beforePhase");
-		}
+	final FacesContext context = event.getFacesContext();
+	final ExternalContext externalContext = context.getExternalContext();
 
-		final FacesContext context = event.getFacesContext();
-		final ExternalContext externalContext = context.getExternalContext();
-		Map externalRequestMap = externalContext.getRequestParameterMap();
+	Map requestMap = externalContext.getRequestMap();
 
-        Map requestMap = externalContext.getRequestMap();
+	if (isPartialRequest(context))
+	{
+	    processPartialPageRequest(context, externalContext, requestMap);
+	} else
+	{
+	    // Iterate over the component tree and set all previously
+	    // transient components to transient again
+	    resetTransientComponents(context.getViewRoot());
+	}
+    }
 
-        if (externalRequestMap.containsKey(PPR_PARAMETER)) {
-			processPartialPageRequest(context, externalContext, requestMap);
-		}
+    /**
+         * if the provided component was marked transient in the last request
+         * set it to transient. Recursively do the same for all children
+         * 
+         * @param comp
+         *                the component to reset
+         */
+    private void resetTransientComponents(UIComponent comp)
+    {
+	if (comp.getAttributes().containsKey(PPRPanelGroupRenderer.TRANSIENT_MARKER_ATTRIBUTE))
+	{
+	    comp.setTransient(true);
 	}
+	for (Iterator iter = comp.getChildren().iterator(); iter.hasNext();)
+	{
+	    UIComponent child = (UIComponent) iter.next();
+	    resetTransientComponents(child);
+	}
+    }
 
-	private void processPartialPageRequest(FacesContext context, final ExternalContext externalContext, Map requestMap) {
-        //If the PhaseListener is invoked the second time do nothing
-        if(requestMap.containsKey(PPRPanelGroupRenderer.PPR_RESPONSE))
-            return;
-        requestMap.put(PPRPanelGroupRenderer.PPR_RESPONSE, Boolean.TRUE);
-
-		ServletResponse response = (ServletResponse) externalContext.getResponse();
-		ServletRequest request = (ServletRequest) externalContext.getRequest();
-
-		UIViewRoot viewRoot = context.getViewRoot();
-		final String characterEncoding = request.getCharacterEncoding();
-		String contentType = getContentType("text/xml", characterEncoding);
-		response.setContentType(contentType);
-		response.setLocale(viewRoot.getLocale());
-		String triggeredComponents = getTriggeredComponents(context);
-		
-		try {
-			PrintWriter out = response.getWriter();
-			context.setResponseWriter(new HtmlResponseWriterImpl(out, contentType, characterEncoding));
-			out.print(XML_HEADER);
-			out.print("<response>\n");
-			encodeTriggeredComponents(out, triggeredComponents, viewRoot, context);
-			out.print("</response>");
-			out.flush();
-		} catch (IOException e) {
-			throw new FacesException(e);
-		}
+    /**
+         * Checks if the currently processed Request is an AJAX request from a
+         * PPRPanelGroup
+         * 
+         * @param context
+         *                the current {@link FacesContext}
+         * @return true if a PPR request is being processed , false otherwise
+         */
+    public static boolean isPartialRequest(FacesContext context)
+    {
+	return context.getExternalContext().getRequestParameterMap().containsKey(PPR_PARAMETER);
+    }
 
-		context.responseComplete();
+    /**
+         * Respond to an AJAX request from a {@link PPRPanelGroup}. The
+         * triggered components are determined by reading the
+         * TRIGGERED_COMPONENTS_PARAMETER from either the RequestParameterMap or
+         * the Request Map. Those componenets are encoded into an XML response.
+         * The lifecycle is quit afterwards.
+         * 
+         * @param context
+         *                the current {@link FacesContext}
+         * @param externalContext
+         *                the current {@link ExternalContext}
+         * @param requestMap
+         *                Map containing the request attributes
+         */
+    private void processPartialPageRequest(FacesContext context, final ExternalContext externalContext, Map requestMap)
+    {
+
+	ServletResponse response = (ServletResponse) externalContext.getResponse();
+	ServletRequest request = (ServletRequest) externalContext.getRequest();
+
+	UIViewRoot viewRoot = context.getViewRoot();
+
+	// Set Character encoding, contentType and locale for the response
+	final String characterEncoding = request.getCharacterEncoding();
+	String contentType = getContentType("text/xml", characterEncoding);
+	response.setContentType(contentType);
+	response.setLocale(viewRoot.getLocale());
+
+	// Fetch the comma-separated list of triggered components
+	String triggeredComponents = getTriggeredComponents(context);
+
+	try
+	{
+	    PrintWriter out = response.getWriter();
+	    context.setResponseWriter(new HtmlResponseWriterImpl(out, contentType, characterEncoding));
+	    out.print(XML_HEADER);
+	    out.print("<response>\n");
+	    encodeTriggeredComponents(out, triggeredComponents, viewRoot, context);
+	    out.print("</response>");
+	    out.flush();
+	} catch (IOException e)
+	{
+	    throw new FacesException(e);
 	}
 
-    private static String getTriggeredComponents(FacesContext fc) {
-        String triggeredComponents = (String) fc.getExternalContext().getRequestMap().get(TRIGGERED_COMPONENTS_PARAMETER);
+	context.responseComplete();
+    }
 
-        if(triggeredComponents == null) {
-            triggeredComponents = (String) fc.getExternalContext().getRequestParameterMap().get(TRIGGERED_COMPONENTS_PARAMETER);
-        }
-
-        return triggeredComponents;
-    }
-
-    public static void addTriggeredComponent(FacesContext fc, String triggeredComponentClientId) {
-        String triggeredComponents = getTriggeredComponents(fc);
-
-        if(triggeredComponents == null || triggeredComponents.trim().length()==0) {
-            triggeredComponents = new String();
-        }
-        else {
-            triggeredComponents = triggeredComponents+",";
-        }
-
-        triggeredComponents = triggeredComponents+triggeredComponentClientId;
-
-        fc.getExternalContext().getRequestMap().put(TRIGGERED_COMPONENTS_PARAMETER, triggeredComponents);
-    }
-
-    private String getContentType(String contentType, String charset) {
-		if (charset == null || charset.trim().length() == 0)
-			return contentType;
-		else
-			return contentType + ";charset=" + charset;
-	}
-
-	private void encodeTriggeredComponents(PrintWriter out, String triggeredComponents, UIViewRoot viewRoot,
-			FacesContext context) {
-		StringTokenizer st = new StringTokenizer(triggeredComponents, ",", false);
-		String clientId;
-		UIComponent component;
-		while (st.hasMoreTokens()) {
-			clientId = st.nextToken();
-			component = viewRoot.findComponent(clientId);
-			if (component != null) {
-				out.print("<component id=\"" + component.getClientId(context) + "\"><![CDATA[");
-				boolean oldValue = HtmlRendererUtils.isAllowedCdataSection(context);
-				HtmlRendererUtils.allowCdataSection(context, false);
-				try {
-                    component.encodeChildren(context);                    
-				} catch (IOException e) {
-					throw new FacesException(e);
-				}
-				HtmlRendererUtils.allowCdataSection(context, oldValue);
-				out.print("]]></component>");
-			} else {
-				log.debug("PPRPhaseListener component with id" + clientId + "not found!");
-			}
-		}
-		out.print("<state>");
-		FacesContext facesContext = FacesContext.getCurrentInstance();
-		StateManager stateManager = facesContext.getApplication().getStateManager();
-		StateManager.SerializedView serializedView = stateManager.saveSerializedView(facesContext);
-		try {
-			stateManager.writeState(facesContext, serializedView);
-		} catch (IOException e) {
-			throw new FacesException(e);
-		}
+    /**
+         * Fetch the comma-separated list of triggered components. They are
+         * either obtained from the Request Parameter Map where they had
+         * previously been set using
+         * {@link PPRPhaseListener#addTriggeredComponent(FacesContext, String))
+         * or from the request parameter map.
+         * 
+         * @param fc
+         *                the current {@link FacesContext}
+         * @return a comma separated list of component IDs of the components
+         *         which are to be updated
+         */
+    private static String getTriggeredComponents(FacesContext fc)
+    {
+	String triggeredComponents = (String) fc.getExternalContext().getRequestMap().get(
+		TRIGGERED_COMPONENTS_PARAMETER);
+
+	if (triggeredComponents == null)
+	{
+	    triggeredComponents = (String) fc.getExternalContext().getRequestParameterMap().get(
+		    TRIGGERED_COMPONENTS_PARAMETER);
+	}
 
-		out.print("</state>");
+	return triggeredComponents;
+    }
 
+    /**
+         * API method for adding triggeredComponents programmatically.
+         * 
+         * @param fc
+         *                the current {@link FacesContext}
+         * @param triggeredComponentClientId
+         *                client ID of the component which is to be updated in
+         *                case of a PPR Response
+         */
+    public static void addTriggeredComponent(FacesContext fc, String triggeredComponentClientId)
+    {
+	String triggeredComponents = getTriggeredComponents(fc);
+
+	if (triggeredComponents == null || triggeredComponents.trim().length() == 0)
+	{
+	    triggeredComponents = new String();
+	} else
+	{
+	    triggeredComponents = triggeredComponents + ",";
 	}
 
-	public PhaseId getPhaseId() {
-		return PhaseId.RENDER_RESPONSE;
+	triggeredComponents = triggeredComponents + triggeredComponentClientId;
+
+	fc.getExternalContext().getRequestMap().put(TRIGGERED_COMPONENTS_PARAMETER, triggeredComponents);
+    }
+
+    /**
+         * Generate content-type String either containing only the mime-type or
+         * mime-type and character enconding.
+         * 
+         * @param contentType
+         *                the contentType/mimeType
+         * @param charset
+         *                the character set
+         * @return the content-type String to be used in an HTTP response
+         */
+    private String getContentType(String contentType, String charset)
+    {
+	if (charset == null || charset.trim().length() == 0)
+	    return contentType;
+	else
+	    return contentType + ";charset=" + charset;
+    }
+
+    /**
+         * Writes the XML elements for the triggered components to the provided
+         * {@link PrintWriter}. Also encode the current state in a separate XML
+         * element.
+         * 
+         * @param out
+         *                the output Writer
+         * @param triggeredComponents
+         *                comma-separated list of component IDs
+         * @param viewRoot
+         *                the current ViewRoot
+         * @param context
+         *                the current {@link FacesContext}
+         */
+    private void encodeTriggeredComponents(PrintWriter out, String triggeredComponents, UIViewRoot viewRoot,
+	    FacesContext context)
+    {
+	StringTokenizer st = new StringTokenizer(triggeredComponents, ",", false);
+	String clientId;
+	UIComponent component;
+	// Iterate over the individual client IDs
+	while (st.hasMoreTokens())
+	{
+	    clientId = st.nextToken();
+	    component = viewRoot.findComponent(clientId);
+	    if (component != null)
+	    {
+		// Write a component tag which contains a CDATA section whith
+		// the rendered HTML
+		// of the component children
+		out.print("<component id=\"" + component.getClientId(context) + "\"><![CDATA[");
+		boolean oldValue = HtmlRendererUtils.isAllowedCdataSection(context);
+		HtmlRendererUtils.allowCdataSection(context, false);
+		try
+		{
+		    component.encodeChildren(context);
+		} catch (IOException e)
+		{
+		    throw new FacesException(e);
+		}
+		HtmlRendererUtils.allowCdataSection(context, oldValue);
+		out.print("]]></component>");
+	    } else
+	    {
+		log.debug("PPRPhaseListener component with id" + clientId + "not found!");
+	    }
+	}
+	// Write the serialized state into a separate XML element
+	out.print("<state>");
+	FacesContext facesContext = FacesContext.getCurrentInstance();
+	StateManager stateManager = facesContext.getApplication().getStateManager();
+	StateManager.SerializedView serializedView = stateManager.saveSerializedView(facesContext);
+	try
+	{
+	    stateManager.writeState(facesContext, serializedView);
+	} catch (IOException e)
+	{
+	    throw new FacesException(e);
 	}
+
+	out.print("</state>");
+
+    }
+
+    public PhaseId getPhaseId()
+    {
+	return PhaseId.RENDER_RESPONSE;
+    }
 }