You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by gc...@apache.org on 2014/02/22 03:26:01 UTC

svn commit: r1570780 - in /myfaces/trinidad/trunk/trinidad-api/src/main: java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts

Author: gcrawford
Date: Sat Feb 22 02:26:01 2014
New Revision: 1570780

URL: http://svn.apache.org/r1570780
Log:
TRINIDAD-2453
MoveChildComponentChange fails to apply document change for relocated component case

thanks to prakash

Modified:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java
    myfaces/trinidad/trunk/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java?rev=1570780&r1=1570779&r2=1570780&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java Sat Feb 22 02:26:01 2014
@@ -38,7 +38,7 @@ import org.w3c.dom.Node;
  * 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 id as the movable child were to be already present in the destination 
+ * the same id as the movable child were to be already present in the destination
  * container, the move operation is aborted.
  * @see #add(FacesContext, ChangeManager)
  * @see ChangeManager#addComponentChange(FacesContext, UIComponent, ComponentChange)
@@ -49,10 +49,10 @@ public final class MoveChildComponentCha
   implements DocumentChange
 {
   /**
-   * Constructs a MoveChildComponentChange. The child will be appended to the 
+   * 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 
+   * @param destinationContainer The destination component into which the child
    * component is to be moved.
    * @throws IllegalArgumentException If movableChild or destinationContainer
    * is null
@@ -73,13 +73,14 @@ public final class MoveChildComponentCha
    * 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.
+   * component is to be moved. This should not be null if the insertBeforeComponent
+   * is null.
    * @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.
+   * @throws IllegalArgumentException If movableChild is null or destinationContainer
+   * and insertBeforeComponent is null, or if a parent component common to 
+   * movableChild and destinationContainer could not be found.
    */
   public MoveChildComponentChange(
     UIComponent movableChild,
@@ -87,110 +88,113 @@ public final class MoveChildComponentCha
     UIComponent insertBeforeComponent)
   {
     if (movableChild == null)
-      throw new IllegalArgumentException(
-        _LOG.getMessage("MOVABLE_CHILD_REQUIRED"));
-
-    if (destinationContainer == null)
-      throw new IllegalArgumentException(
-        _LOG.getMessage("DESTINATION_CONTAINER_REQUIRED"));
-    
-    UIComponent viewRoot = FacesContext.getCurrentInstance().getViewRoot();
-    
-    String sourceAbsoluteLogicalScopedId = ComponentUtils.getLogicalScopedIdForComponent(movableChild, viewRoot);
-    
-    String destinationContainerLogicalPrefix = _getScopedIdPrefix(destinationContainer,
-                                          ComponentUtils.getLogicalScopedIdForComponent(destinationContainer, viewRoot));
+    {
+      throw new IllegalArgumentException(_LOG.getMessage("MOVABLE_CHILD_REQUIRED"));
+    }
     
-    String movableChildId = movableChild.getId();
+    /////////////////////////////
     
-    String destinationAbsoluteLogicalScopedId = (destinationContainerLogicalPrefix != null)
-                                          ? new StringBuilder(destinationContainerLogicalPrefix).
-                                            append(NamingContainer.SEPARATOR_CHAR).
-                                            append(movableChildId).toString()
-                                          : movableChildId;
+    FacesContext context = FacesContext.getCurrentInstance();
 
-    // Get the common parent
-    _commonParent = 
-      _getClosestCommonParentUIXComponent(movableChild, destinationContainer);
-    
-    if (_commonParent == null)
+    // get the doc paths first and validate
+    _moveCompDocPath = ComponentUtils.getDocumentLocationForComponent(context, movableChild);
+    _destinationContainerDocPath = 
+      ComponentUtils.getDocumentLocationForComponent(context, destinationContainer);
+
+    if (_moveCompDocPath == null || _destinationContainerDocPath == null)
+    {
+      // if either components are not in a doc, component is not in the tree, error condition
       throw new IllegalArgumentException(
-        _LOG.getMessage("COMMON_PARENT_NOT_FOUND"));
+        _LOG.getMessage("NO_CONTAINING_DOC_FOUND", 
+                        (_moveCompDocPath == null) ? movableChild : destinationContainer));
+    }
     
-    // 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, viewRoot);
-    if (_movableChildScopedId == null || 
-        _sourceParentScopedId == null || 
+    /////////////////////////////
+
+    // validate destination container
+    destinationContainer = _getValidatedDestinationContainer(destinationContainer, 
+                                                             insertBeforeComponent);
+    // find and validate the common parent
+    UIComponent commonParent = _getValidatedCommonParent(context, movableChild, destinationContainer);
+    _commonParentDocPath = ComponentUtils.getDocumentLocationForComponent(context, commonParent);
+    
+    /////////////////////////////
+    
+    UIComponent viewRoot = context.getViewRoot();
+
+    // Get the scoped id's for move participants (scoped / relative to common parent)
+    _moveCompScopedIdAtSource = ComponentUtils.getScopedIdForComponent(movableChild, commonParent);
+    _moveCompParentScopedId = 
+      ComponentUtils.getScopedIdForComponent(movableChild.getParent(), commonParent);
+    _destinationContainerScopedId = ComponentUtils.getScopedIdForComponent(destinationContainer,
+                                                                           commonParent);
+    _commonParentScopedId = ComponentUtils.getScopedIdForComponent(commonParent, viewRoot);
+    
+    // cannot proceed if we could not get id for the move participants
+    if (_moveCompScopedIdAtSource == null || 
+        _moveCompParentScopedId == null || 
         _destinationContainerScopedId == null ||
         _commonParentScopedId == null)
-      throw new IllegalArgumentException(
-        _LOG.getMessage("MOVE_PARTICIPANTS_WITHOUT_ID"));
+    {
+      throw new IllegalArgumentException(_LOG.getMessage("MOVE_PARTICIPANTS_WITHOUT_ID"));
+    }
+    
+    /////////////////////////////
 
-    // calculate the absolute scoped ids for the source and destination so that we can
-    // handle remapping scoped ids in the SessionChangeManager    
-    String commonParentPrefix = _getScopedIdPrefix(_commonParent, _commonParentScopedId);
+    // get the id path upto the naming container of the common parent so that we can compute the 
+    //  absolute scoped ids from the scoped ids
+    String commonParentPrefix = _getScopedIdPrefix(commonParent, _commonParentScopedId);
       
-    _sourceAbsoluteScopedId = (commonParentPrefix != null)
-                                 ? new StringBuilder(commonParentPrefix).
-                                           append(NamingContainer.SEPARATOR_CHAR).
-                                           append(_movableChildScopedId).toString()
-                                 : _movableChildScopedId;
+    // calculate the absolute scoped ids (scoped from ViewRoot) for the movable componen at its 
+    //  source so that we can handle remapping scoped ids in the SessionChangeManager    
+    _moveCompAbsoluteScopedIdAtSource = (commonParentPrefix != null) ?
+                                          new StringBuilder(commonParentPrefix).
+                                            append(NamingContainer.SEPARATOR_CHAR).
+                                            append(_moveCompScopedIdAtSource).toString()
+                                          : _moveCompScopedIdAtSource;
     
-    _sourceAbsoluteLogicalScopedId = _sourceAbsoluteScopedId.equals(sourceAbsoluteLogicalScopedId) ? null : 
-                                                                                          sourceAbsoluteLogicalScopedId; 
+    // find the logical (id in context of the document where the component is defined) scoped id 
+    _moveCompAbsoluteLogicalScopedIdAtSource = 
+      ComponentUtils.getLogicalScopedIdForComponent(movableChild, viewRoot);
     
-    // calculate the absolute scoped id of the destination
-    String destinationContainerPrefix = _getScopedIdPrefix(destinationContainer,
-                                                           _destinationContainerScopedId);
+    /////////////////////////////
     
-    StringBuilder destinationScopedIdBuilder = new StringBuilder();
+    // calculate the absolute scoped ids of the moveable component at destination after move
+    String destinationContainerPrefix = _getScopedIdPrefix(destinationContainer, 
+                                                           _destinationContainerScopedId);
+    StringBuilder moveCompAtDestinationScopedIdBuilder = new StringBuilder();
     
     if (commonParentPrefix != null)
     {
-      destinationScopedIdBuilder.append(commonParentPrefix).append(NamingContainer.SEPARATOR_CHAR);
+      moveCompAtDestinationScopedIdBuilder.append(commonParentPrefix).
+        append(NamingContainer.SEPARATOR_CHAR);
     }
     
     if (destinationContainerPrefix != null)
     {
-      destinationScopedIdBuilder.append(destinationContainerPrefix).append(NamingContainer.SEPARATOR_CHAR);
+      moveCompAtDestinationScopedIdBuilder.append(destinationContainerPrefix).
+        append(NamingContainer.SEPARATOR_CHAR);
     }
     
-    _destinationAbsoluteScopedId = destinationScopedIdBuilder.append(movableChildId).toString();
-    
-    _destinationAbsoluteLogicalScopedId = _destinationAbsoluteScopedId.equals(destinationAbsoluteLogicalScopedId) ? null :
-                                              destinationAbsoluteLogicalScopedId;
+    String movableChildId = movableChild.getId();
+    _moveCompAbsoluteScopedIdAtDestination = 
+      moveCompAtDestinationScopedIdBuilder.append(movableChildId).toString();
+    String destinationLogicalPrefix = 
+      _getScopedIdPrefix(destinationContainer,
+                         ComponentUtils.getLogicalScopedIdForComponent(destinationContainer, 
+                                                                       viewRoot));
+    
+    // find the logical id
+    _moveCompAbsoluteLogicalScopedIdAtDestination = (destinationLogicalPrefix != null) ? 
+                                                      new StringBuilder(destinationLogicalPrefix).
+                                                        append(NamingContainer.SEPARATOR_CHAR).
+                                                        append(movableChildId).toString()
+                                                      : movableChildId;
+
+    /////////////////////////////
 
     // For insertBeforeComponent, we do not care to obtain scoped id.
-    _insertBeforeId = (insertBeforeComponent == null) ? 
-      null:insertBeforeComponent.getId();
-  }
-  
-  private String _getScopedIdPrefix(UIComponent component, String scopedId)
-  {
-    if (component instanceof NamingContainer)
-      return scopedId;
-    else
-    {
-      // remove the component's id from the end
-      int separatorIndex = scopedId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
-      
-      if (separatorIndex >= 0)
-        return scopedId.substring(0, separatorIndex);
-      else
-      {
-        // component was at top level
-        return null;
-      }
-    }
+    _insertBeforeCompId = (insertBeforeComponent == null) ? null : insertBeforeComponent.getId();
   }
   
   /**
@@ -208,11 +212,8 @@ public final class MoveChildComponentCha
     FacesContext facesContext, 
     ChangeManager changeManager) 
   {
-    UIComponent commonParent = _commonParent;
-
-    if (commonParent == null)
-      commonParent = 
-        facesContext.getViewRoot().findComponent(_commonParentScopedId);
+    UIComponent commonParent = facesContext.getViewRoot().findComponent(_commonParentScopedId);
+    
     if (commonParent == null)
     {
       _LOG.warning("COMMON_PARENT_NOT_FOUND", _commonParentScopedId);
@@ -222,9 +223,6 @@ public final class MoveChildComponentCha
     // 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;
   }
    
@@ -247,8 +245,7 @@ public final class MoveChildComponentCha
       changeTargetComponent.findComponent(_destinationContainerScopedId);
     if(destinationContainer == null)
     {
-      _LOG.warning("DESTINATION_CONTAINER_NOT_FOUND", 
-                   _destinationContainerScopedId);
+      _LOG.warning("DESTINATION_CONTAINER_NOT_FOUND", _destinationContainerScopedId);
       return;
     }
     
@@ -271,10 +268,10 @@ public final class MoveChildComponentCha
     //    of ComponentChange, that could still be in the view tree. There should be one such zombie/ 
     //    duplicate.
     UIComponent sourceParent = 
-      changeTargetComponent.findComponent(_sourceParentScopedId);
+      changeTargetComponent.findComponent(_moveCompParentScopedId);
     
     UIComponent foundChild = 
-      changeTargetComponent.findComponent(_movableChildScopedId);
+      changeTargetComponent.findComponent(_moveCompScopedIdAtSource);
 
     // To flag if a child was already found in a destination container (maybe due to previous move)    
     boolean isChildIdAtDestination = false;
@@ -325,7 +322,7 @@ public final class MoveChildComponentCha
       foundChild.getParent().getChildren().remove(foundChild);
 
       // Try and find the next potential copy of the component to move
-      foundChild = changeTargetComponent.findComponent(_movableChildScopedId);
+      foundChild = changeTargetComponent.findComponent(_moveCompScopedIdAtSource);
     }
     
     //  We need to re-attach the dup for now, the dupes will be eliminated gradually while applying
@@ -338,7 +335,8 @@ public final class MoveChildComponentCha
     // Can't do anything without a movable child.    
     if(movableChild == null)
     {
-      _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _movableChildScopedId);
+      _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _moveCompScopedIdAtSource);
+
       // Reverse any damage that we might have caused, and exit
       if (movedChild != null)
       {
@@ -368,7 +366,7 @@ public final class MoveChildComponentCha
     // 3. Check whether the destination container has a child with same id.
     if (isChildIdAtDestination)
     {
-      _LOG.warning("MOVABLE_CHILD_SAME_ID_FOUND", _movableChildScopedId);
+      _LOG.warning("MOVABLE_CHILD_SAME_ID_FOUND", _moveCompScopedIdAtSource);
 
       // Component type matches, this means the child is already at destination. We have removed all
       //  duplicates, and have nothing more to do in this case
@@ -393,11 +391,11 @@ public final class MoveChildComponentCha
     
     // 4. See if we can find the insertBeforeComponent among the destinationContainer's children
     int insertIndex = -1;
-    if (_insertBeforeId != null)
+    if (_insertBeforeCompId != null)
     {
       for (UIComponent childComponent:destinationContainer.getChildren())
       {
-        if (_insertBeforeId.equals(childComponent.getId()))
+        if (_insertBeforeCompId.equals(childComponent.getId()))
         {
           insertIndex = 
             destinationContainer.getChildren().indexOf(childComponent);
@@ -408,7 +406,7 @@ public final class MoveChildComponentCha
       // insertBeforeId was specified, but we cannot find the insertBefore component. Exit.
       if (insertIndex == -1)
       {
-        _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeId);
+        _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeCompId);
         return;
       }
     }
@@ -436,16 +434,29 @@ public final class MoveChildComponentCha
     if (changeTargetNode == null)
       throw new IllegalArgumentException(_LOG.getMessage("NO_NODE_SPECIFIED"));
 
+    if ( !_moveCompDocPath.equals(_destinationContainerDocPath) ||
+         !_moveCompDocPath.equals(_commonParentDocPath) )
+    {
+      // If all three participants are not in same doc, we cannot proceed with appling doc change.
+      // Throw an exception so that ChangeManagers can handle this failure and do alternate
+      //  processing (eg. add a component change given that doc change failed)
+      throw new IllegalStateException(
+        _LOG.getMessage("MOVE_PARTICIPANTS_NOT_IN_SAME_DOC", 
+                        new Object[] {_moveCompDocPath,
+                                      _destinationContainerDocPath,
+                                      _commonParentDocPath}));
+    }
+
     // Move involves four steps.
     // 1. Finding the child node, the source of move
     Node movableChildNode = 
       ChangeUtils.__findNodeByScopedId(changeTargetNode, 
-                                       _movableChildScopedId, 
+                                       _moveCompScopedIdAtSource, 
                                        Integer.MAX_VALUE);
     
     if(movableChildNode == null)
     {
-      _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _movableChildScopedId);
+      _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _moveCompScopedIdAtSource);
       return;
     }
     
@@ -458,21 +469,20 @@ public final class MoveChildComponentCha
     
     if(destinationContainerNode == null)
     {
-      _LOG.warning("DESTINATION_CONTAINER_NOT_FOUND", 
-                   _destinationContainerScopedId);
+      _LOG.warning("DESTINATION_CONTAINER_NOT_FOUND", _destinationContainerScopedId);
       return;
     }
     
     //3. Finding the neighbor at the destination
-    Node insertBeforeNode = (_insertBeforeId == null) ? 
+    Node insertBeforeNode = (_insertBeforeCompId == null) ? 
       null:ChangeUtils.__findNodeByScopedId(destinationContainerNode, 
-                                            _insertBeforeId, 
+                                            _insertBeforeCompId, 
                                             1);
     // insertBeforeId was specified, but corresponding component is missing.
     //  Abort the move.
-    if(_insertBeforeId != null && insertBeforeNode == null)
+    if(_insertBeforeCompId != null && insertBeforeNode == null)
     {
-      _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeId);
+      _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeCompId);
       return;
     }
 
@@ -491,84 +501,55 @@ public final class MoveChildComponentCha
   }
   
   /**
-   * 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 absolute scopedId of the source component
+   * Returns the absolute scopedId (relative to the ViewRoot) of the movable component as it is 
+   *  before the move
    */
   public String getSourceScopedId()
   {
-    return _sourceAbsoluteScopedId;
+    return _moveCompAbsoluteScopedIdAtSource;
   }
 
-    
   /**
-   * Returns the absolute scopedId of the source component at its destination
+   * Returns the absolute scopedId (relative to the ViewRoot) of the movable component as it would 
+   *  be after the move
    */
   public String getDestinationScopedId()
   {
-    return _destinationAbsoluteScopedId;
+    return _moveCompAbsoluteScopedIdAtDestination;
   }
   
-  
   /**
-   * Returns the absolute logical scopedId of the source component
+   * Returns the absolute logical scopedId of the movable component as it is before the move. 
+   * 
+   * The id returned here will be in context of the document where the component is defined. For 
+   *  example, consider a component that is defined in a base document and is relocated to a 
+   *  different component subtree as in included template (included by an UIXInclude component) 
+   *  via. its facet. In this case the logical id of the move component will be in context of base  
+   *  document (as if it was never relocated) and not the document that defines the including 
+   *  component.
+   *  
+   * @see #getSourceScopedId()
    */
   public String getSourceLogicalScopedId()
   {
-    return (_sourceAbsoluteLogicalScopedId == null) ? _sourceAbsoluteScopedId : _sourceAbsoluteLogicalScopedId;
+    return _moveCompAbsoluteLogicalScopedIdAtSource;
   }
   
   /**
-   * Returns the absolute logical scopedId of the source component at its destination
+   * Returns the absolute logical scopedId of the movable component as it would be after the move.
+   * 
+   * The id returned here will be in context of the document where the component is defined. For 
+   *  example, consider a component that is defined in a base document and is relocated to a 
+   *  different component subtree as in included template (included by an UIXInclude component) 
+   *  via. its facet. In this case the logical id of the move component will be in context of base  
+   *  document (as if it was never relocated) and not the document that defines the including 
+   *  component.
+   *  
+   * @see #getDestinationScopedId()
    */
   public String getDestinationLogicalScopedId()
   {
-    return (_destinationAbsoluteLogicalScopedId == null) ? _destinationAbsoluteScopedId : _destinationAbsoluteLogicalScopedId;
+    return _moveCompAbsoluteLogicalScopedIdAtDestination;
   }
   
   @Override
@@ -582,27 +563,211 @@ public final class MoveChildComponentCha
     
     MoveChildComponentChange other = (MoveChildComponentChange)o;
     
-    return getSourceLogicalScopedId().equals(other.getSourceLogicalScopedId()) &&
-           getDestinationLogicalScopedId().equals(other.getDestinationLogicalScopedId()) &&
-           _equalsOrNull(_insertBeforeId, other._insertBeforeId);
+    return  _equalsOrNull(_moveCompScopedIdAtSource, other._moveCompScopedIdAtSource) &&
+            _equalsOrNull(_moveCompAbsoluteScopedIdAtSource, 
+                          other._moveCompAbsoluteScopedIdAtSource) &&
+            _equalsOrNull(_moveCompAbsoluteLogicalScopedIdAtSource, 
+                          other._moveCompAbsoluteLogicalScopedIdAtSource) &&
+            _equalsOrNull(_moveCompDocPath, other._moveCompDocPath) &&
+            _equalsOrNull(_moveCompParentScopedId, other._moveCompParentScopedId) &&
+            _equalsOrNull(_moveCompAbsoluteScopedIdAtDestination, 
+                          other._moveCompAbsoluteScopedIdAtDestination) &&
+            _equalsOrNull(_moveCompAbsoluteLogicalScopedIdAtDestination, 
+                          other._moveCompAbsoluteLogicalScopedIdAtDestination) &&
+            _equalsOrNull(_destinationContainerScopedId, other._destinationContainerScopedId) &&
+            _equalsOrNull(_destinationContainerDocPath, other._destinationContainerDocPath) &&
+            _equalsOrNull(_commonParentScopedId, other._commonParentScopedId) &&
+            _equalsOrNull(_commonParentDocPath, other._commonParentDocPath) &&
+            _equalsOrNull(_insertBeforeCompId, other._insertBeforeCompId);
   }
   
   @Override
   public int hashCode()
   {
-    int hashCode = getSourceLogicalScopedId().hashCode() + 37 * getDestinationLogicalScopedId().hashCode();
-    if (_insertBeforeId != null)
-    {
-      hashCode = hashCode + 1369 * _insertBeforeId.hashCode();
-    }
-    return hashCode;
+    return ((_moveCompScopedIdAtSource == null) ? 0 : _moveCompScopedIdAtSource.hashCode()) + 
+            37 * ((_moveCompAbsoluteScopedIdAtSource == null) ? 
+                  0 : _moveCompAbsoluteScopedIdAtSource.hashCode()) +
+            37 * ((_moveCompAbsoluteLogicalScopedIdAtSource == null) ? 
+                  0 : _moveCompAbsoluteLogicalScopedIdAtSource.hashCode()) +
+            37 * ((_moveCompDocPath == null) ? 0 : _moveCompDocPath.hashCode()) +
+            37 * ((_moveCompParentScopedId == null) ? 
+                  0 : _moveCompParentScopedId.hashCode()) +
+            37 * ((_moveCompAbsoluteScopedIdAtDestination == null) ? 
+                  0 : _moveCompAbsoluteScopedIdAtDestination.hashCode()) +
+            37 * ((_moveCompAbsoluteLogicalScopedIdAtDestination == null) ? 
+                  0 : _moveCompAbsoluteLogicalScopedIdAtDestination.hashCode()) +
+            37 * ((_destinationContainerScopedId == null) ? 
+                  0 : _destinationContainerScopedId.hashCode()) +
+            37 * ((_destinationContainerDocPath == null) ? 
+                  0 : _destinationContainerDocPath.hashCode()) +
+            37 * ((_commonParentScopedId == null) ? 0 : _commonParentScopedId.hashCode()) +
+            37 * ((_commonParentDocPath == null) ? 0 : _commonParentDocPath.hashCode()) +
+            37 * ((_insertBeforeCompId == null) ? 0 : _insertBeforeCompId.hashCode());
   }
       
   @Override
   public String toString()
   {
-    return super.toString() + "[logical_source=" + getSourceLogicalScopedId() + " logical_destination=" + getDestinationLogicalScopedId() +
-            " absolute source=" + getSourceScopedId() + " absolute destination" + getDestinationScopedId() +" insert_before=" + _insertBeforeId + "]";
+    StringBuffer sb = new StringBuffer();
+    
+    sb.append(super.toString());
+    sb.append("[moveCompAbsoluteLogicalScopedIdAtSource=").
+       append(_moveCompAbsoluteScopedIdAtSource);
+    sb.append(" moveCompAbsoluteLogicalScopedIdAtDestination=").
+       append(_moveCompAbsoluteLogicalScopedIdAtDestination);
+    sb.append(" moveCompAbsoluteScopedIdAtSource=").append(_moveCompAbsoluteScopedIdAtSource);
+    sb.append(" moveCompAbsoluteScopedIdAtDestination=").
+       append(_moveCompAbsoluteScopedIdAtDestination);
+    sb.append(" insertBeforeCompId=").append(_insertBeforeCompId);
+    sb.append(" commonParentScopedId=").append(_commonParentScopedId);
+    sb.append(" moveCompDocPath=").append(_moveCompDocPath);
+    sb.append(" destinationContainerDocPath=").append(_destinationContainerDocPath);
+    
+    return sb.append("]").toString();
+  }
+
+  /**
+   * Returns best common parent of the two supplied components in a subtree to which this move 
+   * change can be added.
+   * - If the supplied components belong to same document, we try to get a common parent that is of
+   *   type UIXComponent and belongs to the same document, this is to be able to support applying 
+   *   document change.
+   * - If they do not belong to same document, we just return the closest common UIComponent parent, 
+   *   this should suffice to apply component change.
+   * 
+   * @throws IllegalArgumentException if we are not able to find the common parent for the supplied
+   *          components
+   */
+  private UIComponent _getValidatedCommonParent(
+    FacesContext context,
+    UIComponent componentToMove,
+    UIComponent parentAtDestination) 
+  {
+    // Calculate the depth of each node.
+    int firstDepth = _computeDepth(componentToMove);
+    int secondDepth = _computeDepth(parentAtDestination);
+           
+    // Move the deeper of the two components to its ancestor at the same depth
+    // as the shallower.
+    if (secondDepth > firstDepth)
+    {
+      parentAtDestination = _getAncestor(parentAtDestination, secondDepth - firstDepth);
+    }
+    else if(secondDepth < firstDepth)
+    {
+      componentToMove = _getAncestor(componentToMove, firstDepth - secondDepth);
+    }
+
+    // Crawl up until we find the shared ancestor.
+    while (componentToMove != null && (componentToMove != parentAtDestination))
+    {
+      componentToMove = componentToMove.getParent();
+      parentAtDestination = parentAtDestination.getParent();
+    }
+    
+    UIComponent commonParent = componentToMove;
+    
+    // try to find a better ancestor complying these two conditions
+    //  1. The common parent is a UIXComponent instance - only UIXComponents have tags
+    //  2. The common parent belongs to same document that the other two participating components 
+    //     belong to
+    if (_moveCompDocPath.equals(_destinationContainerDocPath))
+    {
+      commonParent = _getUIXComponentAncestorInDoc(context, commonParent);
+    }
+    
+    // we cannot proceed if we could not find the common parent
+    if (commonParent == null)
+    {
+      throw new IllegalArgumentException(_LOG.getMessage("COMMON_PARENT_NOT_FOUND"));
+    }
+    
+    return commonParent;
+  }
+  
+  /**
+   * For a supplied base component, returns the closest UIXComponent ancestor that is in same 
+   * document as base component. If no such component is found, returns the supplied base component.
+   */
+  private UIComponent _getUIXComponentAncestorInDoc(
+    FacesContext context, 
+    UIComponent baseComp)
+  {
+    UIComponent ancestor = baseComp;
+    
+    while (ancestor != null)
+    {
+      if (ancestor instanceof UIXComponent &&
+          _moveCompDocPath.equals(ComponentUtils.getDocumentLocationForComponent(context, 
+                                                                                 ancestor)))
+      {
+        return ancestor;
+      }
+
+      ancestor = ancestor.getParent();
+    }
+    
+    return baseComp;
+  }
+  
+  /**
+   * Trims the supplied scoped id of the supplied component until its immediate naming container
+   *  and returns.
+   */
+  private String _getScopedIdPrefix(UIComponent component, String scopedId)
+  {
+    if (component instanceof NamingContainer)
+      return scopedId;
+    else
+    {
+      // remove the component's id from the end
+      int separatorIndex = scopedId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
+      
+      if (separatorIndex >= 0)
+        return scopedId.substring(0, separatorIndex);
+      else
+      {
+        // component was at top level
+        return null;
+      }
+    }
+  }
+  
+  private boolean _equalsOrNull(Object obj1, Object obj2)
+  {
+    return (obj1 == null) ? (obj2 == null) : obj1.equals(obj2);
+  }
+
+  /**
+   * Returns the destination container if passed non-null after doing needed validations. If null 
+   * destinationContainer is passed, determines it from the supplied insertBeforeComponent. 
+   */
+  private static UIComponent _getValidatedDestinationContainer(
+    UIComponent destinationContainer, 
+    UIComponent insertBeforeComponent)
+  {
+    if (insertBeforeComponent != null)
+    {
+      UIComponent parent = insertBeforeComponent.getParent();
+      
+      if (destinationContainer == null)
+      {
+        destinationContainer = parent;
+      }
+      
+      // if container was supplied, it better be parent of component to move next to
+      else if (destinationContainer != parent)
+      {
+        throw new IllegalArgumentException(
+          _LOG.getMessage("DESTINATION_CONTAINER_NOT_INSERTBEFORES_PARENT"));
+      }
+    }
+    else if (destinationContainer == null)
+    {
+      throw new IllegalArgumentException(_LOG.getMessage("DESTINATION_CONTAINER_REQUIRED"));
+    }
+    
+    return destinationContainer;
   }
   
   /**
@@ -638,24 +803,36 @@ public final class MoveChildComponentCha
     return component;
   }
   
-  private boolean _equalsOrNull(Object obj1, Object obj2)
-  {
-    return (obj1 == null) ? (obj2 == null) : obj1.equals(obj2);
-  }
+  // *ScopedId -> the id of the component scoped / relative to the common parent
+  // *AbsoluteScopedId -> the id of the component scoped / relative to the ViewRoot
+  // *AbsoluteLogicalScopedId -> id of the component scoped / relative to the ViewRoot in context
+  //    of the document in which it is originally defined. For more details, see documentation in 
+  //    getSourceLogicalScopedId() 
+  
+  // for component to be moved
+  private final String _moveCompScopedIdAtSource;
+  private final String _moveCompAbsoluteScopedIdAtSource;
+  private final String _moveCompAbsoluteLogicalScopedIdAtSource;
+  private final String _moveCompDocPath;
+  private final String _moveCompParentScopedId;
+
+  // for component at destination after the move
+  private final String _moveCompAbsoluteScopedIdAtDestination;
+  private final String _moveCompAbsoluteLogicalScopedIdAtDestination;
   
-  private transient UIComponent _commonParent;
-
-  private final String _movableChildScopedId;
-  private final String _sourceParentScopedId;
+  // for new parent at destination
   private final String _destinationContainerScopedId;
+  private final String _destinationContainerDocPath;
+
+  // for parent common to the source and new parent at destination
   private final String _commonParentScopedId;
-  private final String _insertBeforeId;
-  private final String _sourceAbsoluteScopedId;
-  private final String _destinationAbsoluteScopedId;
-  private final String _sourceAbsoluteLogicalScopedId;
-  private final String _destinationAbsoluteLogicalScopedId;
+  private final String _commonParentDocPath;
+
+  // for immediate sibling of moved component at destination
+  private final String _insertBeforeCompId;
+
   private static final long serialVersionUID = 1L;
 
-  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
-    MoveChildComponentChange.class);
+  private static final TrinidadLogger _LOG = 
+    TrinidadLogger.createTrinidadLogger(MoveChildComponentChange.class);
 }

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts?rev=1570780&r1=1570779&r2=1570780&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts Sat Feb 22 02:26:01 2014
@@ -244,11 +244,20 @@
  <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>
+ <resource key="DESTINATION_CONTAINER_REQUIRED">Destination container component is required if the component to move next to is not supplied.</resource>
+
+ <!-- DESTINATION_CONTAINER_NOT_INSERTBEFORES_PARENT -->
+ <resource key="DESTINATION_CONTAINER_NOT_INSERTBEFORES_PARENT">Destination container should be same as the parent of component to move next to.</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>
 
+ <!-- NO_CONTAINING_DOC_FOUND -->
+ <resource key="NO_CONTAINING_DOC_FOUND">The supplied component {0} is not in the component tree.</resource>
+
+ <!-- MOVE_PARTICIPANTS_NOT_IN_SAME_DOC -->
+ <resource key="MOVE_PARTICIPANTS_NOT_IN_SAME_DOC">Document change was not added because the participating components are not defined in the same document. The component being moved is in {0}, the parent into which it is to be moved is in {1} and common parent is in {2}.</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>