You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ma...@apache.org on 2008/11/11 08:44:56 UTC

svn commit: r712972 - in /myfaces/trinidad/trunk_1.2.x: trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/ trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/ trini...

Author: matzew
Date: Mon Nov 10 23:44:55 2008
New Revision: 712972

URL: http://svn.apache.org/viewvc?rev=712972&view=rev
Log:
TRINIDAD-1243 - Adding a change implementation to the ChangeManager framework

Thx to Prakash Udupa N for his patch

Added:
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java
Removed:
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeMarker.java
Modified:
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/BaseChangeManager.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeManager.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeUtils.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentTag.java
    myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts
    myfaces/trinidad/trunk_1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/change/NullChangeManager.java

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java Mon Nov 10 23:44:55 2008
@@ -24,6 +24,7 @@
 
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
+
 /**
  * Change specialization for adding a child component.
  * While applying this Change, the child component is re-created and added to
@@ -89,10 +90,13 @@
     //  to-be-added child, remove it and get the new one added.
     UIComponent removableChild = ChangeUtils.getChildForId(uiComponent, newChildId);
   
+    // Users can add component themselves in addition to adding a ComponentChange
+    //  This could cause duplicates, which is fine. Handle this gracefully with 
+    //  a info log and replacement
     if (removableChild != null)
     {
-      throw new IllegalStateException(_LOG.getMessage(
-        "ATTEMP_ADD_DUPLICATE_ID", newChildId));
+      _LOG.info("ATTEMPT_ADD_CHILD_WITH_DUPLICATE_ID", newChildId);
+      children.remove(removableChild);
     }
     
     if (_insertBeforeId == null)

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/BaseChangeManager.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/BaseChangeManager.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/BaseChangeManager.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/BaseChangeManager.java Mon Nov 10 23:44:55 2008
@@ -18,23 +18,18 @@
  */
 package org.apache.myfaces.trinidad.change;
 
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import javax.faces.component.NamingContainer;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 
-import org.w3c.dom.Document;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
+import org.w3c.dom.Document;
+
 
 /**
  * Base ChangeManager implementation that manages the bookkeeping for
  * supporting both ComponentChanges and DocumentChanges.
- * subclasses must implement getComponentToChangesMapForView to implement
+ * subclasses must implement addComponentChangeImpl() to implement
  * the ComponentChange support.  To support DocumentChanges,
  * <code>getDocument</code> must be implemented.
  *
@@ -45,7 +40,6 @@
   /**
    * {@inheritDoc}
    */
-  // TODO : Maybe we need to allow adding Changes for specific viewId's
   @Override
   public void addComponentChange(
     FacesContext facesContext,
@@ -57,7 +51,7 @@
         "CANNOT_ADD_CHANGE_WITH_FACECONTEXT_OR_UICOMPONENT_OR_NULL"));
 
     // add the change to the component
-    _addComponentChangeImpl(facesContext, uiComponent, change);
+    addComponentChangeImpl(facesContext, uiComponent, change);
 
     if (supportsDocumentPersistence(facesContext))
     {
@@ -81,89 +75,19 @@
   }
 
   /**
-   * {@inheritDoc}
+   * A no-op implementation of adding a ComponentChange. Sub-classers should
+   * override and provide an implementation if they support component changes.
+   * @param facesContext The FacesContext for this request.
+   * @param targetComponent The target component against which this change needs 
+   * to be registered and applied later on.
+   * @param componentChange The ComponentChange to add
    */
-  @Override
-  public Iterator<ComponentChange> getComponentChanges(
+   protected void addComponentChangeImpl(
     FacesContext facesContext,
-    UIComponent uiComponent)
-  {
-    if (uiComponent == null)
-      return null;
-
-    String viewId = facesContext.getViewRoot().getViewId();
-    Map<String, List<ComponentChange>> componentToChangesMap =
-       getComponentToChangesMapForView(facesContext, viewId, false);
-
-    if (componentToChangesMap == null)
-      return null;
-
-    String uniqueIdForComponent = _getUniqueIdForComponent(uiComponent);
-    List<ComponentChange> changesList = componentToChangesMap.get(uniqueIdForComponent);
-    if (changesList == null)
-      return null;
-
-    return changesList.iterator();
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public Iterator<String> getComponentIdsWithChanges(FacesContext facesContext)
+    UIComponent targetComponent,
+    ComponentChange componentChange)
   {
-    String viewId = facesContext.getViewRoot().getViewId();
-    Map<String, List<ComponentChange>> componentToChangesMap =
-       getComponentToChangesMapForView(facesContext, viewId, false);
-
-    if (componentToChangesMap == null)
-      return null;
-
-    return componentToChangesMap.keySet().iterator();
-  }
-
-  /**
-   * The Map used to store the Changes.  The Map is stored as
-   * key=ComponentCompositeId, value=ChangesList (List)
-   *
-   * @param facesContext FacesContext for request
-   * @param viewId viewID for request
-   * @param createIfNecessary <code>true</code> if Map should be created if not
-   *        already present
-   * @return Map of componentID tokens to Lists of Changes
-   */
-  protected abstract Map<String, List<ComponentChange>> getComponentToChangesMapForView(
-    FacesContext facesContext,
-    String viewId,
-    boolean createIfNecessary);
-
-  /**
-   * Implementation of adding a ComponentChange
-   * @param change ComponentChange to add
-   */
-  private void _addComponentChangeImpl(
-    FacesContext facesContext,
-    UIComponent uiComponent,
-    ComponentChange change)
-  {
-    String viewId = facesContext.getViewRoot().getViewId();
-
-    Map<String, List<ComponentChange>> componentToChangesMap = getComponentToChangesMapForView(facesContext,
-                                                                viewId,
-                                                                true);
-
-    String uniqueIdForComponent = _getUniqueIdForComponent(uiComponent);
-
-    List<ComponentChange> changeListForComponent = 
-      componentToChangesMap.get(uniqueIdForComponent);
-
-    if (changeListForComponent == null)
-    {
-      changeListForComponent = new CopyOnWriteArrayList<ComponentChange>();
-      componentToChangesMap.put(uniqueIdForComponent, changeListForComponent);
-    }
-
-    changeListForComponent.add(change);
+    //no-op
   }
 
   // =-= bts Testing hack hook
@@ -173,26 +97,6 @@
     // noop
   }
 
-  static private String _getUniqueIdForComponent(UIComponent uiComponent)
-  {
-    //pu: If this component were to be a NamingContainer, we treat itself as its
-    //  own closest ancestor that is a NamingContainer. Matches algorithm of
-    //  UIComponent.findComponent().
-    UIComponent ancestor = uiComponent;
-    StringBuffer uniqueIdBuffer = new StringBuffer();
-    uniqueIdBuffer.append(uiComponent.getId());
-    while (ancestor != null)
-    {
-      if (ancestor instanceof NamingContainer)
-      {
-        uniqueIdBuffer.insert(0,new StringBuffer().append(ancestor.getId()).
-          append(NamingContainer.SEPARATOR_CHAR));
-      }
-      ancestor = ancestor.getParent();
-    }
-    return uniqueIdBuffer.toString();
-  }
-
   /**
    * Override to return the Document to modify as part of document-based
    * persistence.

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeManager.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeManager.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeManager.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeManager.java Mon Nov 10 23:44:55 2008
@@ -19,13 +19,16 @@
 package org.apache.myfaces.trinidad.change;
 
 import java.util.HashMap;
-import java.util.Iterator;
+
+import javax.el.ValueExpression;
 
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
+import javax.faces.el.ValueBinding;
 
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
+
 /**
  * The base class for all ChangeManagers.
  * A ChangeManager should manage accumulation of Changes and also
@@ -132,8 +135,6 @@
     return null;
   }
 
-
-
   /**
    * Add a ComponentChange to this current request for a specified component.
    * @throws IllegalArgumentException if any of the supplied parameters were to
@@ -160,26 +161,16 @@
   }
 
   /**
-   * Retrieve the ComponentChanges available for specified component on this
-   *  request.
-   * @return An Iterator of ComponentChanges in the order in which they
-   *         are associated with the UIComponent.
-   *         Returns <code>null<code> if there are no such Changes
+   * Applies all the ComponentChanges added so far for the current view.
+   * Developers should not need to call this method. Internal implementation
+   * will call it as the component tree is built and is ready to take changes.
+   * @param facesContext The FacesContext instance for the current request.
    */
-  public abstract Iterator<ComponentChange> getComponentChanges(
-    FacesContext facesContext,
-    UIComponent uiComponent);
-
-  /**
-  * Retrieve the identifiers of all components on this request that have Changes
-  *  associated with them for the viewId specified in the facesContext.
-  * @param facesContext
-  * @return An Iterator that can be used to access the collection of component
-  *          identifiers. Returns null if there are no such components.
-  */
-  public abstract Iterator<String> getComponentIdsWithChanges(
-    FacesContext facesContext);
-
+  public void applyComponentChangesForCurrentView(FacesContext facesContext)
+  {
+    throw new UnsupportedOperationException("Subclassers must implement");
+  }
+  
   private static class AttributeConverter extends DocumentChangeFactory
   {
     @Override
@@ -192,18 +183,26 @@
         Object value = change.getAttributeValue();
 
         // =-= bts TODO add registration of attribute converters
+        String valueString = null;
         if ((value == null) ||
             (value instanceof CharSequence) ||
             (value instanceof Number) ||
             (value instanceof Boolean))
         {
-          String valueString = (value != null)
-                                ? value.toString()
-                                : null;
-
+          valueString = (value != null)? value.toString() : null;
+        }
+        else if (value instanceof ValueExpression)
+        {
+          valueString = ((ValueExpression)value).getExpressionString();
+        }
+        else if (value instanceof ValueBinding)
+        {
+          valueString = ((ValueBinding)value).getExpressionString();
+        }
+        
+        if (valueString != null)
           return new AttributeDocumentChange(change.getAttributeName(),
                                              valueString);
-        }
       }
 
       // no conversion possible

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeUtils.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeUtils.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/ChangeUtils.java Mon Nov 10 23:44:55 2008
@@ -20,19 +20,21 @@
 
 import java.util.List;
 
+import javax.faces.component.NamingContainer;
 import javax.faces.component.UIComponent;
 
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
-import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
 
 /**
  * Utility functions for use by Changes.
  * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/change/ChangeUtils.java#0 $) $Date: 10-nov-2005.19:09:58 $
  */
-class ChangeUtils 
+final class ChangeUtils 
 {
   private ChangeUtils()
   {
@@ -119,6 +121,50 @@
   }
   
   /**
+   * Search the supplied Node and its descendants for an Element Node with the 
+   * scopedTargetId.
+   * @param baseNode The base Node of the subtree relative to which the target
+   * Node is to be found.
+   * @param scopedTargetId The scoped id of the target node which is to be found. 
+   * This id should be relative from the base Node of the search, with 
+   * NamingContainer.SEPARATOR_CHAR being the separator for fragments. If the
+   * targetId starts with a NamingContainer.SEPARATOR_CHAR, it is considered
+   * as an absolute id, and the owner Document will be the base Node of search.
+   * Examples of scopedTargetId values: 'foo:bar:baz'/':foo:bar:baz'/'foo'.
+   * @param searchDepth The integer which indicates till how many levels deeper 
+   * from the baseNode, the search has to be performed.
+   * @return The target Node with the given scopedTargetId if found within the
+   * permitted searchDepth, else null 
+   */
+  static Node __findNodeByScopedId(
+    Node baseNode,
+    String scopedTargetId,
+    int searchDepth)
+  {
+    if (baseNode == null || 
+        scopedTargetId == null || 
+        scopedTargetId.length() == 0)
+      return null;
+     
+    // Check if we have received an absolute id.
+    if (NamingContainer.SEPARATOR_CHAR == scopedTargetId.charAt(0))
+    {
+      // If so directly deal with the owner Document.
+      if (baseNode.getNodeType() != Node.DOCUMENT_NODE)
+        baseNode = baseNode.getOwnerDocument();
+
+      // Remove leading ':'
+      scopedTargetId = scopedTargetId.substring(1);
+    }
+
+    // 'foo:bar:baz' -> ['foo'],['bar'},['baz']
+    String[] idFrags = 
+      scopedTargetId.split(String.valueOf(NamingContainer.SEPARATOR_CHAR));
+    
+    return _traceNodeByIdPath(baseNode, idFrags, 0, searchDepth);
+  }
+  
+  /**
    * Given a node representing a component, returns the named facet's Element.
    * @param componentNode The node to search for a facet contained in it.
    * @param facetName The name of the facet to search for.
@@ -171,7 +217,67 @@
     }
   }
 
+  /**
+   * Traces for the targetNode recursively in the subtree rooted by baseNode.
+   * Trace path is also controlled by the id path fragments and the fragIndex. 
+   * Trace stops when the permitted searchDepth is reached, or when the trace 
+   * faled.
+   * @returns The traget node of the search. Returns null if the target Node is
+   * not to be found, or if we have exceeded the search depth.
+   */
+  private static Node _traceNodeByIdPath(
+    Node baseNode,
+    String[] idFrags,
+    int fragIndex,
+    int searchDepth)
+  {
+    if ((baseNode.getNodeType() == Node.ELEMENT_NODE) && 
+        idFrags[fragIndex].equals(((Element)baseNode).getAttribute(_ID_ATTRIB_NAME)))
+    {
+      if (idFrags.length == fragIndex + 1)
+      {
+        // This is the node for the last of the id fragments, so we found the 
+        // target now.
+        return baseNode;
+      }
+      else 
+      {
+        // This is the intermediate node matching the path, start looking for 
+        //  nodes with id's matching rest of fragments.
+        fragIndex++;
+      }
+    }
+
+    // Check child Nodes
+    if (searchDepth > 0)
+    {
+      searchDepth--;
+
+      Node currChild  = baseNode.getFirstChild();
+     
+      while (currChild != null)
+      {
+        if (Node.ELEMENT_NODE == currChild.getNodeType())
+        {
+          Node targetNode = _traceNodeByIdPath(currChild,
+                                               idFrags,
+                                               fragIndex,
+                                               searchDepth);
+          if (targetNode != null)
+            return targetNode;
+        }
+
+        currChild = currChild.getNextSibling();
+      }
+    }
+    
+    // We are past the permitted search depth, or we searched the entire subtree 
+    // in vain, abort.
+    return null;
+  }
+
   static final String __JSF_CORE_NAMESPACE = "http://java.sun.com/jsf/core";
+  private static final String _ID_ATTRIB_NAME = "id";
   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
     ChangeUtils.class);
 }

Added: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java?rev=712972&view=auto
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java (added)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java Mon Nov 10 23:44:55 2008
@@ -0,0 +1,435 @@
+package org.apache.myfaces.trinidad.change;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.component.UIXComponent;
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.util.ComponentUtils;
+
+import org.w3c.dom.Node;
+
+
+/**
+ * Change specialization for moving a child from one container to another.
+ * MoveChildComponent should be registered on a parent component that is
+ * common to the child being moved and the container component at destination.
+ * In other words, while calling addComponentChange() or addDocumentChange()
+ * methods on the ChangeManager to add a MoveChildComponentChange, the common
+ * parent component instance must be passed as an argument. The add() utility
+ * method in this class can be alternatively used to conveniently register the
+ * change against the common parent. While applying this change, if a child with
+ * the same identifier as the movable child were to be already present in the
+ * destination container, it will be considered as a duplicate child, will be
+ * removed and movable child will be added.
+ * @see #add(FacesContext, ChangeManager)
+ * @see ChangeManager#addComponentChange(FacesContext, UIComponent, ComponentChange)
+ * @see ChangeManager#addDocumentChange(FacesContext, UIComponent, DocumentChange)
+ */
+public final class MoveChildComponentChange 
+  extends ComponentChange
+  implements DocumentChange
+{
+  /**
+   * Constructs a MoveChildComponentChange. The child will be appended to the 
+   * list of children of the destinationContainer.
+   * @param movableChild The child component to be moved.
+   * @param destinationContainer The destination component into which the child 
+   * component is to be moved.
+   * @throws IllegalArgumentException If movableChild or destinationContainer
+   * is null
+   */
+  public MoveChildComponentChange(
+    UIComponent movableChild,
+    UIComponent destinationContainer)
+  {
+    this(movableChild, destinationContainer, null);
+  }
+  
+  /**
+   * Constructs a MoveChildComponentChange. The child will be inserted to the 
+   * list of children of the destinationContainer, before the supplied 
+   * insertBeforecomponent. If the supplied insertBeforeComponent is null, the 
+   * child will be appended to the list of children of the destinationContainer.
+   * If the insertBeforeComponent is non-null, and if it were not to be found
+   * while applying this change, the movableChild will not be moved.
+   * @param movableChild The child component to be moved.
+   * @param destinationContainer The destination component into which the child 
+   * component is to be moved.
+   * @param insertBeforeComponent The component before which the moved child is
+   * to be inserted. This can be null, in which case the movableChild is
+   * appended.
+   * @throws IllegalArgumentException If movableChild or destinationContainer
+   * is null, or if a parent component common to movableChild and 
+   * destinationContainer could not be found.
+   */
+  public MoveChildComponentChange(
+    UIComponent movableChild,
+    UIComponent destinationContainer, 
+    UIComponent insertBeforeComponent)
+  {
+    if (movableChild == null)
+      throw new IllegalArgumentException(
+        _LOG.getMessage("MOVABLE_CHILD_REQUIRED"));
+
+    if (destinationContainer == null)
+      throw new IllegalArgumentException(
+        _LOG.getMessage("DESTINATION_CONTAINER_REQUIRED"));
+
+    // Get the common parent
+    _commonParent = 
+      _getClosestCommonParentUIXComponent(movableChild, destinationContainer);
+    
+    if (_commonParent == null)
+      throw new IllegalArgumentException(
+        _LOG.getMessage("COMMON_PARENT_NOT_FOUND"));
+    
+    // Get the scoped id's for all participants
+    _movableChildScopedId = 
+      ComponentUtils.getScopedIdForComponent(movableChild, _commonParent);
+    _sourceParentScopedId = 
+      ComponentUtils.getScopedIdForComponent(movableChild.getParent(), 
+                                            _commonParent);
+    _destinationContainerScopedId = 
+      ComponentUtils.getScopedIdForComponent(destinationContainer, _commonParent);
+    _commonParentScopedId = 
+      ComponentUtils.getScopedIdForComponent(_commonParent, null);
+
+    if (_movableChildScopedId == null || 
+        _sourceParentScopedId == null || 
+        _destinationContainerScopedId == null ||
+        _commonParentScopedId == null)
+      throw new IllegalArgumentException(
+        _LOG.getMessage("MOVE_PARTICIPANTS_WITHOUT_ID"));
+
+    // For insertBeforeComponent, we do not care to obtain scoped id.
+    _insertBeforeId = (insertBeforeComponent == null) ? 
+      null:insertBeforeComponent.getId();
+  }
+  
+  /**
+   * Convenience method to add this MoveChildComponentChange to the supplied
+   * ChangeManager. The change will be registered against a parent component
+   * that is common to the child being moved and the container component at
+   * destination.
+   * @param facesContext The FacesContext instance for the current request
+   * @param changeManager The ChangeManager instance on which this
+   * MoveChildComponentChange is to be added.
+   * @return The common parent component against which this 
+   * MoveChildComponentChange was registered.
+   */
+  public UIComponent add(
+    FacesContext facesContext, 
+    ChangeManager changeManager) 
+  {
+    UIComponent commonParent = _commonParent;
+
+    if (commonParent == null)
+      commonParent = 
+        facesContext.getViewRoot().findComponent(_commonParentScopedId);
+    if (commonParent == null)
+    {
+      _LOG.warning("COMMON_PARENT_NOT_FOUND", _commonParentScopedId);
+      return null;
+    }
+    
+    // Register a move change against the common parent
+    changeManager.addComponentChange(facesContext, commonParent, this);
+    
+    // We dont need to keep the common parent anymore
+    _commonParent = null;
+    
+    return commonParent;
+  }
+   
+  /**
+   * Apply this change to the specified component.
+   * @param changeTargetComponent The component that is a common parent to the 
+   * movable child and the destination container.
+   * @throws IllegalArgumentException If the supplied changeTargetComponent
+   * is null.
+   */
+  @Override
+  public void changeComponent(UIComponent changeTargetComponent)
+  {
+    if (changeTargetComponent == null)
+      throw new IllegalArgumentException(
+        _LOG.getMessage("COMPONENT_REQUIRED"));
+    
+    // 1. Check for destination container component 
+    UIComponent destinationContainer = 
+      changeTargetComponent.findComponent(_destinationContainerScopedId);
+    if(destinationContainer == null)
+    {
+      _LOG.warning("DESTINATION_CONTAINER_NOT_FOUND", 
+                   _destinationContainerScopedId);
+      return;
+    }
+    
+    // 2. Find movableChild, gather any duplicates and remove them.
+    //  Duplicates are possible because 
+    //  a) taghandlers re-create the component that was in the jspx file in
+    //    their original location, no matter whether it was moved/removed due to 
+    //    aplication of a different ComponentChange. Such components could now 
+    //    be considered duplicates. In theory, there could be just one such 
+    //    duplicate.
+    //  b) We would have moved/added components due to an earlier application of 
+    //    a ComponentChange, that could still be in the view tree. Such 
+    //    components must now be considered duplicates. In theory, there could 
+    //    be just one such duplicate.
+    // This issue of duplicates is more common when the movement is within same 
+    // NamingContainer.
+    UIComponent sourceParent = 
+      changeTargetComponent.findComponent(_sourceParentScopedId);
+    
+    UIComponent foundChild = 
+      changeTargetComponent.findComponent(_movableChildScopedId);
+    //Assume the first found child is the movableChild
+    UIComponent movableChild = foundChild;
+    int movableChildIndex = 0;
+    while (foundChild != null)
+    {
+      // If the parent matches, this is the one to move, rest are duplicates
+      if (foundChild.getParent().equals(sourceParent))
+      {
+        movableChild = foundChild;
+        movableChildIndex = sourceParent.getChildren().indexOf(movableChild);
+      }
+
+      // Invariably, remove the found component from the tree. We remove the
+      //  movableChild also, otherwise, findComponent blind loops on this same 
+      //  component if movableChild and duplicates are within same immediate
+      //  NamingContainer.
+      foundChild.getParent().getChildren().remove(foundChild);
+
+      // Try and find the next potential duplicate
+      foundChild = changeTargetComponent.findComponent(_movableChildScopedId);
+    }
+    
+    if(movableChild == null)
+    {
+      _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _movableChildScopedId);
+      return;
+    }
+    
+    // Reattach the moveable child, so that move happens atomically at the end.
+    sourceParent.getChildren().add(movableChildIndex, movableChild);
+    
+    // 3. If there is a child already existing with the same identifier in the 
+    //  destination container, remove it. We are doing this before identifying 
+    //  the insert index so that insert index is accurate, in case we end up 
+    //  removing any child in this step.
+    String movableChildId = movableChild.getId();
+    int indexOfChildWithSameIdAtDestination = 0;
+    UIComponent childWithSameIdAtDestination = null;
+    for (UIComponent childComponent:destinationContainer.getChildren())
+    {
+      if (movableChildId.equals(childComponent.getId()))
+      {
+        indexOfChildWithSameIdAtDestination = 
+          destinationContainer.getChildren().indexOf(childComponent);
+        childWithSameIdAtDestination = childComponent;
+        destinationContainer.getChildren().remove(childComponent);
+      }
+    }
+    
+    // 4. See if we can find the insertBeforeComponent among the 
+    //  destinationContainer's children
+    int insertIndex = -1;
+    if (_insertBeforeId != null)
+    {
+      for (UIComponent childComponent:destinationContainer.getChildren())
+      {
+        if (_insertBeforeId.equals(childComponent.getId()))
+        {
+          insertIndex = 
+            destinationContainer.getChildren().indexOf(childComponent);
+          break;
+        }
+      }
+  
+      // insertBeforeId was specified, but corresponding component is missing. 
+      //  In this case abort the move, after re-adding any components in 
+      //  destination container that we may have deleted from step #3.
+      if (insertIndex == -1)
+      {
+        if (childWithSameIdAtDestination != null)
+          destinationContainer.getChildren().add(
+            indexOfChildWithSameIdAtDestination,
+            childWithSameIdAtDestination);
+        
+        _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeId);
+        return;
+      }
+    }
+    
+    // 5. Atomically move the child
+    if (insertIndex == -1)
+      destinationContainer.getChildren().add(movableChild);
+    else
+      destinationContainer.getChildren().add(insertIndex, movableChild);
+  }
+  
+  /**
+   * Given the DOM Node representing a Component, apply any necessary
+   * DOM changes. The node passed will be the Node that is a common parent for
+   * the movable child and the destination container.
+   * There is a limitation with the document change, that the movable child 
+   * Node, destination container Node, and the common parent Node have to belong
+   * to the same document.
+   * @param changeTargetNode DOM Node that is a common parent for the movable
+   * child and the destination container.
+   * @throws IllegalArgumentException If changeTargeNode were to be null.
+   */
+  public void changeDocument(Node changeTargetNode)
+  {
+    if (changeTargetNode == null)
+      throw new IllegalArgumentException(_LOG.getMessage("NO_NODE_SPECIFIED"));
+
+    // Move involves four steps.
+    // 1. Finding the child node, the source of move
+    Node movableChildNode = 
+      ChangeUtils.__findNodeByScopedId(changeTargetNode, 
+                                       _movableChildScopedId, 
+                                       Integer.MAX_VALUE);
+    
+    if(movableChildNode == null)
+    {
+      _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _movableChildScopedId);
+      return;
+    }
+    
+    // 2. Finding the destination container node
+    Node destinationContainerNode = 
+      ChangeUtils.__findNodeByScopedId(changeTargetNode, 
+                                       _destinationContainerScopedId, 
+                                       Integer.MAX_VALUE);
+
+    
+    if(destinationContainerNode == null)
+    {
+      _LOG.warning("DESTINATION_CONTAINER_NOT_FOUND", 
+                   _destinationContainerScopedId);
+      return;
+    }
+    
+    //3. Finding the neighbor at the destination
+    Node insertBeforeNode = (_insertBeforeId == null) ? 
+      null:ChangeUtils.__findNodeByScopedId(destinationContainerNode, 
+                                            _insertBeforeId, 
+                                            1);
+    // insertBeforeId was specified, but corresponding component is missing.
+    //  Abort the move.
+    if(_insertBeforeId != null && insertBeforeNode == null)
+    {
+      _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeId);
+      return;
+    }
+
+    //4. Atomically move the child.
+    destinationContainerNode.insertBefore(movableChildNode, insertBeforeNode);
+  }
+
+  /** 
+   * Returns true if adding the DocumentChange should force the JSP Document
+   * to reload
+   * @return true Since moving of components should force the document to reload
+   */
+  public boolean getForcesDocumentReload()
+  {
+    return true;
+  }
+  
+  /**
+   * Returns the first UIXComponent common parent of two components in a
+   * subtree.
+   * @param firstComponent The first UIComponent instance
+   * @param secondComponent The second UIComponent instance
+   * @return UIComponent The closest common parent of the two supplied 
+   * components.
+   */
+  private static UIComponent _getClosestCommonParentUIXComponent(
+    UIComponent firstComponent,
+    UIComponent secondComponent) 
+  {
+    if (firstComponent == null || secondComponent == null)
+      return null;
+
+    // Calculate the depth of each node.
+    int firstDepth = _computeDepth(firstComponent);
+    int secondDepth = _computeDepth(secondComponent);
+           
+    // Move the deeper of the two components to its ancestor at the same depth
+    // as the shallower.
+    if (secondDepth > firstDepth)
+    {
+      secondComponent = _getAncestor(secondComponent, secondDepth - firstDepth);
+    }
+    else if(secondDepth < firstDepth)
+    {
+      firstComponent = _getAncestor(firstComponent, firstDepth - secondDepth);
+    }
+
+    // Crawl up until we find the shared ancestor.
+    while (firstComponent != null && (firstComponent != secondComponent))
+    {
+      firstComponent = firstComponent.getParent();
+      secondComponent = secondComponent.getParent();
+    }
+
+    // Crawl up to first UIXComponent shared parent, since only UIXComponents 
+    // have tags that apply changes.
+    UIComponent sharedRoot = firstComponent;
+
+    while ((sharedRoot != null) && !(sharedRoot instanceof UIXComponent))
+      sharedRoot = sharedRoot.getParent();
+          
+    return sharedRoot;
+  }
+  
+  /**
+   * Returns the depth of a UIComponent in the tree. 
+   * @param comp the UIComponent whose depth has to be calculated
+   * @return the depth of the passed in UIComponent
+   */
+  private static int _computeDepth(UIComponent comp) 
+  {
+    int i = 0;
+    while((comp = comp.getParent()) != null) 
+    {
+      i++;
+    }
+    return i;
+  }
+
+  /**
+   * Returns the nth ancestor of the passed in component.
+   * @param component The UIComponent whose nth ancestor has to be found
+   * @param level Indicates how many levels to go up from the component
+   * @return The nth ancestor of the component
+   */
+  private static UIComponent _getAncestor(UIComponent component, int level) 
+  {
+    assert(level >= 0);
+    
+    while(level > 0)
+    {
+      component = component.getParent();
+      level--;
+    }
+    return component;
+  }
+  
+  private transient UIComponent _commonParent;
+
+  private final String _movableChildScopedId;
+  private final String _sourceParentScopedId;
+  private final String _destinationContainerScopedId;
+  private final String _commonParentScopedId;
+  private final String _insertBeforeId;
+  
+  private static final long serialVersionUID = 1L;
+
+  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
+    MoveChildComponentChange.class);
+}

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java Mon Nov 10 23:44:55 2008
@@ -18,14 +18,24 @@
  */
 package org.apache.myfaces.trinidad.change;
 
+import java.io.Serializable;
+
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewRoot;
 import javax.faces.context.FacesContext;
 
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.util.ComponentUtils;
+
 import org.w3c.dom.Document;
 
+
 /**
  * A ChangeManager implementation that manages
  *  persisting the added Changes at the session. This means
@@ -35,48 +45,66 @@
 public class SessionChangeManager extends BaseChangeManager
 {
   /**
-   * The Map used to store the Changes.  The Map is stored as
-   * key=ComponentCompositeId, value=ChangesList (List)
-   * 
-   * @param facesContext FacesContext for request
-   * @param viewId viewID for request
-   * @param createIfNecessary <code>true</code> if Map should be created if not
-   *        already present
-   * @return Synchronized Map of componentID tokens to Lists of Changes
+   * {@inheritDoc}
+   * @param facesContext The FacesContext instance for the current request.
    */
-  @SuppressWarnings("unchecked")
   @Override
-  protected Map<String, List<ComponentChange>> getComponentToChangesMapForView(
-    FacesContext facesContext,
-    String viewId,
-    boolean createIfNecessary)
+  public void applyComponentChangesForCurrentView(FacesContext facesContext)
   {
-    Map<String, Object> sessMap = facesContext.getExternalContext().getSessionMap();
-    //pu: Get datastructure #1 described at the end of this file.
-    Map<String, Map<String, List<ComponentChange>>> viewToChangesMap = 
-      (Map<String, Map<String, List<ComponentChange>>>)sessMap.get(_CHANGE_KEY);
-    if (viewToChangesMap == null)
-    {
-      if (!createIfNecessary)
-        return null;
-      viewToChangesMap = new ConcurrentHashMap<String, Map<String, List<ComponentChange>>>();
-      sessMap.put(_CHANGE_KEY, viewToChangesMap);
-    }
+    UIViewRoot viewRoot = facesContext.getViewRoot();
+    
+    List<QualifiedComponentChange> componentChangesForView = 
+      _getComponentChangesForView(facesContext, viewRoot.getViewId(), false);
     
-    //pu: Get datastructure #2 described at the end of this file.
-    Map<String, List<ComponentChange>> componentToChangesMap = viewToChangesMap.get(viewId);
-    if (componentToChangesMap == null)
+    for (QualifiedComponentChange qualifiedChange : componentChangesForView)
     {
-      if (!createIfNecessary)
-        return null;
-      componentToChangesMap = new ConcurrentHashMap<String, List<ComponentChange>>();
-      viewToChangesMap.put(viewId, componentToChangesMap);
+      UIComponent targetComponent = 
+        viewRoot.findComponent(qualifiedChange.getTargetComponentScopedId());
+      // Possible that the target component no more exists in the view
+      if (targetComponent != null)
+      {
+        ComponentChange componentChange = qualifiedChange.getComponentChange();
+        componentChange.changeComponent(targetComponent);
+      }
+      else
+      {
+        _LOG.info(this.getClass().getName(),
+                  "applyComponentChangesForCurrentView",
+                  "TARGET_COMPONENT_MISSING_CHANGE_FAILED",
+                  qualifiedChange.getTargetComponentScopedId());
+      }
     }
-    return componentToChangesMap;
+  }
+
+  /**
+   * Adds a ComponentChange and registers against the supplied component.
+   * Changes added thus live at Session scope.
+   * Use applyComponentChangesForCurrentView() to apply these changes.
+   * @param facesContext The FacesContext instance for the current request.
+   * @param targetComponent The target component against which this change needs 
+   * to be registered and applied later on.
+   * @param componentChange The ComponentChange to add.
+   */
+  protected void addComponentChangeImpl(
+    FacesContext facesContext,
+    UIComponent targetComponent,
+    ComponentChange componentChange)
+  {
+    String viewId = facesContext.getViewRoot().getViewId();
+    
+    List<QualifiedComponentChange> componentChangesForView = 
+      _getComponentChangesForView(facesContext, viewId, true);
+
+    String scopedIdForTargetComponent = 
+      ComponentUtils.getScopedIdForComponent(targetComponent, null);
+
+    componentChangesForView.add(
+      new QualifiedComponentChange(scopedIdForTargetComponent, 
+                                   componentChange));
   }
 
   /** 
-   * We don't support DocumentAspect persistence
+   * We don't support DocumentChange persistence
    */
   @Override
   protected Document getDocument(FacesContext context)
@@ -84,14 +112,96 @@
     return null;
   }
 
+  /**
+   * Gets the in-order list of component changes for the given view.
+   * @param facesContext The FacesContext instance for this request.
+   * @param viewId The id of the view for which changes are required.
+   * @param createIfNecessary Indicates whether the underlying datastructures
+   * that store the component changes needs to be created if absent.
+   * @return The in-order list of component changes for the supplied view. This
+   * will be in the same order in which the component changes were added through
+   * calls to <code>addComponentChange()</code>.
+   */
+  private List<QualifiedComponentChange> _getComponentChangesForView(
+    FacesContext facesContext,
+    String viewId,
+    boolean createIfNecessary)
+  {
+    Object session = facesContext.getExternalContext().getSession(true);
+    
+    // Key is view id and value is list of component changes for that view
+    ConcurrentHashMap<String, List<QualifiedComponentChange>> 
+      componentChangesMapForSession;
+    
+    synchronized(session)
+    {
+      Map<String, Object> sessMap = 
+        facesContext.getExternalContext().getSessionMap();
+      
+      componentChangesMapForSession = 
+        (ConcurrentHashMap<String, List<QualifiedComponentChange>>)
+          (sessMap.get(_COMPONENT_CHANGES_MAP_FOR_SESSION_KEY));
+      
+      if (componentChangesMapForSession == null)
+      {
+        if (!createIfNecessary)
+          return Collections.emptyList();
+  
+        componentChangesMapForSession = 
+          new ConcurrentHashMap<String, List<QualifiedComponentChange>>();
+        sessMap.put(_COMPONENT_CHANGES_MAP_FOR_SESSION_KEY, 
+                    componentChangesMapForSession);
+      }
+    }
+
+    if (!componentChangesMapForSession.containsKey(viewId))
+    {
+      if (!createIfNecessary)
+        return Collections.emptyList();
+      
+      // Writes are per change addition, not very frequent, using 
+      // CopyOnWriteArrayList should suffice.
+      componentChangesMapForSession.putIfAbsent(
+        viewId,
+        new CopyOnWriteArrayList<QualifiedComponentChange>());
+    }
+    
+    return componentChangesMapForSession.get(viewId);
+  }
   
-  static private final String _CHANGE_KEY =
-    "org.apache.myfaces.trinidadinternal.Change";
+  private static final class QualifiedComponentChange implements Serializable
+  {
+    public QualifiedComponentChange(String targetComponentScopedId,
+                                    ComponentChange componentChange)
+    {
+      // NO-TRANS : Class is private and inner, no translated message required
+      if (targetComponentScopedId == null || componentChange == null)
+        throw new IllegalArgumentException("Target component scoped id and " +
+                                           "component change is required");
+      
+      _targetComponentScopedId = targetComponentScopedId;
+      _componentChange = componentChange;
+    }
+    
+    public String getTargetComponentScopedId()
+    {
+      return _targetComponentScopedId;
+    }
+
+    public ComponentChange getComponentChange()
+    {
+      return _componentChange;
+    }
+
+    private final String _targetComponentScopedId;
+    private final ComponentChange _componentChange;
+
+    private static final long serialVersionUID = 1L;
+  }
+
+  private static final String _COMPONENT_CHANGES_MAP_FOR_SESSION_KEY =
+    "org.apache.myfaces.trinidadinternal.ComponentChangesMapForSession";
     
-  //pu: DataStructure:
-  //  1. Session holds a Map instance named by _CHANGE_KEY 
-  //      [ key=viewId(String), value=ComponenChangesMap(Map) ]
-  //  2. ComponenChangesMap is a Map 
-  //      [ key=ComponentCompositeId, value=ChangesList (List) ]
-  //  3. ChangesList is a List of Changes for the given component.
+  private static final TrinidadLogger _LOG = 
+    TrinidadLogger.createTrinidadLogger(SessionChangeManager.class);
 }

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java Mon Nov 10 23:44:55 2008
@@ -25,32 +25,27 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.TimeZone;
 
 import javax.el.MethodExpression;
 import javax.el.ValueExpression;
 
-import javax.faces.component.NamingContainer;
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
 import javax.faces.context.FacesContext;
-import javax.faces.webapp.UIComponentClassicTagBase;
 import javax.faces.webapp.UIComponentELTag;
 
 import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.Tag;
 
 import org.apache.myfaces.trinidad.bean.FacesBean;
 import org.apache.myfaces.trinidad.bean.PropertyKey;
-import org.apache.myfaces.trinidad.change.AddComponentChange;
-import org.apache.myfaces.trinidad.change.ChangeMarker;
-import org.apache.myfaces.trinidad.change.ComponentChange;
+import org.apache.myfaces.trinidad.change.ChangeManager;
 import org.apache.myfaces.trinidad.component.UIXComponent;
+import org.apache.myfaces.trinidad.component.UIXDocument;
 import org.apache.myfaces.trinidad.context.RequestContext;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
+
 /**
  * Subclass of UIComponentTag to add convenience methods,
  * and optimize where appropriate.
@@ -83,8 +78,14 @@
   public int doEndTag() throws JspException
   {
     UIComponent component = getComponentInstance();
-    if (getCreated() || _forceApplyChanges)
-      _applyChanges(getFacesContext(), component);
+    
+    // Apply changes once we have a stable UIComponent subtree is completely 
+    //  created. End of document tag is a best bet.
+    if (component instanceof UIXDocument)
+    {
+      ChangeManager cm = RequestContext.getCurrentInstance().getChangeManager();
+      cm.applyComponentChangesForCurrentView(FacesContext.getCurrentInstance());
+    }
     return super.doEndTag();
   }
 
@@ -399,151 +400,6 @@
     return list.toArray(new String[list.size()]);
   }
 
-  /**
-   * Locate and return the nearest enclosing UIComponentClassicTagBase for a given tag if any;
-   *  otherwise, return null.
-   * @param tag The tag to start the walk up from
-   * @return The nearest enclosing UIXComponentELTag instance
-   */
-  private UIComponentClassicTagBase _getParentUIComponentClassicTagBase(
-    UIComponentClassicTagBase tag)
-  {
-    if (tag == null)
-      return null;
-
-    Tag result = tag.getParent();
-    while (result != null)
-    {
-      if (result instanceof UIComponentClassicTagBase)
-        return (UIComponentClassicTagBase) result;
-      result = result.getParent();
-    }
-    return null;
-  }
-
-  /**
-   * Walks up the tree, starting from a supplied startTag and finds an enclosing tag matching the
-   * supplied absolute idPath.
-   * @param startTag The tag from where to start the tag tree walk up from.
-   * @param idPath   An array of id's that represents the absolute id of the enclosing tag.
-   * @return         The UIXComponentELTag that matches the absolute id if find was successful, 
-   *                  null otherwise.
-   */
-  private UIXComponentELTag _findEnclosingTag(
-    UIXComponentELTag startTag,
-    String[] idPath)
-  {
-    if (startTag == null || idPath == null || idPath.length == 0)
-      return null;
-
-    LinkedList<UIComponentClassicTagBase> enclosingTags = 
-      new LinkedList<UIComponentClassicTagBase>();
-    // The last entry will be the id of the tag to be found.
-    String targetId = idPath[idPath.length - 1];
-
-    // Step 1: Move up and build a list of all enclosing tags for the startTag.
-    for(UIComponentClassicTagBase currComponentTag = startTag;
-        currComponentTag != null;
-        currComponentTag = _getParentUIComponentClassicTagBase(currComponentTag))
-    {
-      enclosingTags.addFirst(currComponentTag);
-    }
-    
-    // Bail out if we could not build a trace path of enclosing tags.
-    if (enclosingTags.isEmpty())
-      return null;
-
-    // Step 2: Walk forward the list of enclosing tags to find the target.
-    Iterator<UIComponentClassicTagBase> iter = enclosingTags.iterator();
-
-    // Step 2a: If there are tags corresponding to NamingContainers in list, skip the expected
-    //  number of such NamingContainer tags.
-    int namingContainerIndex = 0;
-    for(UIComponentClassicTagBase currComponentTag = iter.next();
-        currComponentTag != null;
-        currComponentTag = iter.next())
-    {
-      UIComponent currComponent = currComponentTag.getComponentInstance();
-      
-      if (currComponent instanceof NamingContainer)
-      {
-        // If the id of this 'NamingContainer' tag does not match the next segment in sequence,
-        //  we would never be able to reach target, bail out.
-        if (! currComponent.getId().equals(idPath[namingContainerIndex]))
-          return null;
-        else
-        {
-          namingContainerIndex++;
-          if (namingContainerIndex == idPath.length - 1)
-            break;
-        }
-      }
-    }
-
-    // Did not find all of our path elements, something very messed up.
-    if (namingContainerIndex < idPath.length - 1)
-      return null;
-    
-    // Step 2b: Look for the target tag from here on.
-    for (UIComponentClassicTagBase currComponentTag = iter.next();
-         currComponentTag != null;
-         currComponentTag = iter.next())
-    {
-      if (targetId.equals(currComponentTag.getComponentInstance().getId()))
-      {
-        // If we are here, we reached the target, and it is very likely a UIXComponentELTag.
-        return (currComponentTag instanceof UIXComponentELTag) ? 
-          (UIXComponentELTag)currComponentTag : null;
-      }
-    }
-
-    // If we reached here, we could not find the target.
-    return null;
-  }
-
-  private void _applyChanges(
-    FacesContext facesContext, 
-    UIComponent uiComponent)
-  {
-    RequestContext afc = RequestContext.getCurrentInstance();
-    Iterator<ComponentChange> changeIter =
-      afc.getChangeManager().getComponentChanges(facesContext, uiComponent);
-
-    if (changeIter == null)
-      return;
-    while (changeIter.hasNext())
-    {
-      ComponentChange change = changeIter.next();
-
-      // If this is just a marker change, find and flag the target component.
-      if (change instanceof ChangeMarker)
-      {
-        String id = ((ChangeMarker) change).getChangeTargetComponentScopedId();
-        String[] idPath = id.split(String.valueOf(NamingContainer.SEPARATOR_CHAR));
-        UIXComponentELTag targetTag = _findEnclosingTag(this, idPath);
-        if (targetTag != null)
-          targetTag._forceApplyChanges = true;
-      }
-      else
-      {
-        change.changeComponent(uiComponent);
-
-        // In case this Change has added a new component/facet, the added
-        //  component could have its own Changes, that may need to be applied here.
-        if (change instanceof AddComponentChange)
-        {
-          UIComponent newAddedComponent =
-            ((AddComponentChange) change).getComponent();
-
-          if (newAddedComponent != null)
-          {
-            _applyChanges(facesContext, newAddedComponent);
-          }
-        }
-      }
-    }
-  }
-
   private static final TrinidadLogger _LOG = 
     TrinidadLogger.createTrinidadLogger(UIXComponentELTag.class);
 
@@ -557,9 +413,6 @@
     return sdf;
   }
 
-  // Use if change application needs to be forced on the tag regardless of whether the 
-  //  corresponding component is newly created during tag execution.
-  private boolean           _forceApplyChanges;
   private MethodExpression  _attributeChangeListener;
   private String            _validationError;
 }

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentTag.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentTag.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentTag.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentTag.java Mon Nov 10 23:44:55 2008
@@ -24,13 +24,11 @@
 
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.Iterator;
 
 import javax.el.MethodExpression;
 
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
-import javax.faces.context.FacesContext;
 import javax.faces.el.MethodBinding;
 import javax.faces.el.ValueBinding;
 import javax.faces.webapp.UIComponentTag;
@@ -40,14 +38,11 @@
 
 import org.apache.myfaces.trinidad.bean.FacesBean;
 import org.apache.myfaces.trinidad.bean.PropertyKey;
-import org.apache.myfaces.trinidad.change.AddComponentChange;
-import org.apache.myfaces.trinidad.change.AttributeComponentChange;
-import org.apache.myfaces.trinidad.change.ComponentChange;
 import org.apache.myfaces.trinidad.component.UIXComponent;
-import org.apache.myfaces.trinidad.context.RequestContext;
 import org.apache.myfaces.trinidad.event.AttributeChangeEvent;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
+
 /**
  * Subclass of UIComponentTag to add convenience methods,
  * and optimize where appropriate.
@@ -112,24 +107,6 @@
   }
 
   @Override
-  public int doEndTag() throws JspException
-  {
-    UIComponent component = getComponentInstance();
-    if (isSuppressed() && getCreated())
-      _applyChanges(getFacesContext(), component);
-    return super.doEndTag();
-  }
-
-  @Override
-  protected void encodeBegin() throws java.io.IOException
-  {
-    UIComponent component = getComponentInstance();
-    if (!isSuppressed() && getCreated())
-      _applyChanges(getFacesContext(), component);
-    super.encodeBegin();
-  }
-
-  @Override
   protected final void setProperties(UIComponent component)
   {
     if (component instanceof UIViewRoot)
@@ -562,37 +539,6 @@
     return list.toArray(new String[list.size()]);
   }
 
-  private static void _applyChanges(
-    FacesContext facesContext,
-    UIComponent uiComponent)
-  {
-    RequestContext afc = RequestContext.getCurrentInstance();
-    Iterator<ComponentChange> changeIter =
-                  afc.getChangeManager().getComponentChanges(facesContext, uiComponent);
-
-    if (changeIter == null)
-      return;
-    while (changeIter.hasNext())
-    {
-      ComponentChange change = changeIter.next();
-      
-      change.changeComponent(uiComponent);
-      
-      //pu: In case this Change has added a new component/facet, the added
-      //  component could have its own Changes, that may need to be applied here.
-      if (change instanceof AddComponentChange)
-      {
-        UIComponent newAddedComponent =
-          ( (AddComponentChange)change ).getComponent();
-
-        if (newAddedComponent != null)
-        {
-          _applyChanges(facesContext, newAddedComponent);
-        }
-      }
-    }
-  }
-
   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentTag.class);
 
   // We rely strictly on ISO 8601 formats

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts Mon Nov 10 23:44:55 2008
@@ -177,8 +177,8 @@
  <!-- INVALID_INDEX -->
  <resource key="INVALID_INDEX">Invalid index</resource>
  
- <!-- ATTEMP_ADD_DUPLICATE_ID -->
- <resource key="ATTEMP_ADD_DUPLICATE_ID">Attempt to add a duplicate ID {0}</resource>
+ <!-- ATTEMPT_ADD_CHILD_WITH_DUPLICATE_ID -->
+ <resource key="ATTEMPT_ADD_CHILD_WITH_DUPLICATE_ID">Adding a child when a child with same ID already exists. Old child will be removed and new child added. {0}</resource>
  
  <!-- NO_NODE_SPECIFIED -->
  <resource key="NO_NODE_SPECIFIED">No node specified</resource>
@@ -225,6 +225,27 @@
  <!-- FACET_NAME_MUST_SPECIFIED -->
  <resource key="FACET_NAME_MUST_SPECIFIED">Facet name must be specified</resource>
  
+ <!-- MOVABLE_CHILD_REQUIRED -->
+ <resource key="MOVABLE_CHILD_REQUIRED">Child component to be moved is required.</resource>
+
+ <!-- DESTINATION_CONTAINER_REQUIRED -->
+ <resource key="DESTINATION_CONTAINER_REQUIRED">Destination container component is required.</resource>
+
+ <!-- MOVE_PARTICIPANTS_WITHOUT_ID -->
+ <resource key="MOVE_PARTICIPANTS_WITHOUT_ID">One or more of the participating components in MoveChildComponentChange does not have id.</resource>
+
+ <!-- COMMON_PARENT_NOT_FOUND -->
+ <resource key="COMMON_PARENT_NOT_FOUND">Could not find a common parent between the movable child and destination container: {0}.</resource>
+
+ <!-- MOVABLE_CHILD_NOT_FOUND -->
+ <resource key="MOVABLE_CHILD_NOT_FOUND">Child to be moved could not be found: {0}.</resource>
+
+ <!-- DESTINATION_CONTAINER_NOT_FOUND -->
+ <resource key="DESTINATION_CONTAINER_NOT_FOUND">Destination container could not be found: {0}.</resource>
+
+ <!-- INSERT_BEFORE_NOT_FOUND -->
+ <resource key="INSERT_BEFORE_NOT_FOUND">Insert before component or node could not be found: {0}.</resource>
+
  <!-- INDEX_SIZE -->
  <resource key="INDEX_SIZE">index:{0} size:{1}</resource>
  

Modified: myfaces/trinidad/trunk_1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/change/NullChangeManager.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk_1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/change/NullChangeManager.java?rev=712972&r1=712971&r2=712972&view=diff
==============================================================================
--- myfaces/trinidad/trunk_1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/change/NullChangeManager.java (original)
+++ myfaces/trinidad/trunk_1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/change/NullChangeManager.java Mon Nov 10 23:44:55 2008
@@ -18,14 +18,13 @@
  */
 package org.apache.myfaces.trinidadinternal.change;
 
-import java.util.Iterator;
-
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 
 import org.apache.myfaces.trinidad.change.ChangeManager;
 import org.apache.myfaces.trinidad.change.ComponentChange;
 
+
 /**
  * An ChangeManager implementation that is all a no-op.
  *
@@ -43,23 +42,14 @@
   {
     // do nothing
   }
-  
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public Iterator<ComponentChange> getComponentChanges(FacesContext facesContext,
-                             UIComponent uiComponent)
-  {
-    return null;
-  }
-  
+
   /**
    * {@inheritDoc}
+   * @param facesContext The FacesContext instance for the current request.
    */
   @Override
-  public Iterator<String> getComponentIdsWithChanges(FacesContext facesContext)
+  public void applyComponentChangesForCurrentView(FacesContext facesContext)
   {
-    return null;
+    //no-op
   }
 }