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 2010/06/16 17:27:21 UTC

svn commit: r955269 - in /myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main: java/org/apache/myfaces/trinidad/change/ java/org/apache/myfaces/trinidad/webapp/ xrts/org/apache/myfaces/trinidad/resource/

Author: gcrawford
Date: Wed Jun 16 15:27:21 2010
New Revision: 955269

URL: http://svn.apache.org/viewvc?rev=955269&view=rev
Log:
TRINIDAD-1784 Session ChangeManager should not apply attribute customizations for cases when it is not needed

Modified:
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SetFacetChildComponentChange.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java?rev=955269&r1=955268&r2=955269&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/AddChildComponentChange.java Wed Jun 16 15:27:21 2010
@@ -28,7 +28,8 @@ import org.apache.myfaces.trinidad.loggi
 /**
  * Change specialization for adding a child component.
  * While applying this Change, the child component is re-created and added to
- *  the list of children.
+ *  the list of children. If a child component with an id same as the new child being added is 
+ *  already present in the parent container, the new child is not added.
  * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/change/AddChildComponentChange.java#0 $) $Date: 10-nov-2005.19:09:54 $
  */
 public class AddChildComponentChange extends AddComponentChange 
@@ -86,17 +87,15 @@ public class AddChildComponentChange ext
     String newChildId = child.getId();
     List<UIComponent> children = uiComponent.getChildren();
     
-    //pu: If there were to be a child already with the ID same as the
-    //  to-be-added child, remove it and get the new one added.
-    UIComponent removableChild = ChangeUtils.getChildForId(uiComponent, newChildId);
+    // If there were to be a child already with the ID same as the to-be-added child, it might have
+    //  been added from previous change application, and further customizations might have happened
+    //  on them. We just want to warn, abort the child addition, and not alter the component tree.
+    UIComponent duplicateChild = 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)
+    if (duplicateChild != null)
     {
       _LOG.info("ATTEMPT_ADD_CHILD_WITH_DUPLICATE_ID", newChildId);
-      children.remove(removableChild);
+      return;
     }
     
     if (_insertBeforeId == null)

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java?rev=955269&r1=955268&r2=955269&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/MoveChildComponentChange.java Wed Jun 16 15:27:21 2010
@@ -38,9 +38,8 @@ 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 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.
+ * 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)
  * @see ChangeManager#addDocumentChange(FacesContext, UIComponent, DocumentChange)
@@ -113,8 +112,8 @@ public final class MoveChildComponentCha
       ComponentUtils.getScopedIdForComponent(destinationContainer, _commonParent);
           
     _commonParentScopedId = 
-      ComponentUtils.getScopedIdForComponent(_commonParent, null);
-
+      ComponentUtils.getScopedIdForComponent(_commonParent, 
+                                             FacesContext.getCurrentInstance().getViewRoot());
     if (_movableChildScopedId == null || 
         _sourceParentScopedId == null || 
         _destinationContainerScopedId == null ||
@@ -233,35 +232,71 @@ public final class MoveChildComponentCha
       return;
     }
     
-    // 2. Find movableChild, gather any duplicates and remove them.
+    // 2. Find movableChild, gather the possible duplicates (theoritically only three) and keep a
+    //  single copy among 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 
+    //    be considered duplicates, because they are newly created from their vanilla definition
+    //    in the jspx document, and would not have any further ComponentChanges applied on them. 
+    //    There should be one such duplicate.
+    //  b) Apart from adding the MoveComponentChange, we expect developers to apply the change in 
+    //    order to reflect in the same request cycle (i.e. developers call 
+    //    ComponentChange.changeComponent()). Consequently, the component tree contains a moved 
+    //    child at the destination. Such components must be preserved, because they have 
+    //    incorporated any subsequent ComponentChanges on them. There should be one such moved
+    //    component.
+    //  c) We would have moved/added components due to previous customization an earlier application 
+    //    of ComponentChange, that could still be in the view tree. There should be one such zombie/ 
     //    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;
+
+    // To flag if a child was already found in a destination container (maybe due to previous move)    
+    boolean isChildIdAtDestination = false;
+    
+    UIComponent movableChild = null;
     int movableChildIndex = 0;
+    UIComponent movedChild = null;
+    int movedChildIndex = 0;
+    UIComponent duplicateChild = null;
+    int duplicateChildIndex = 0;
+    UIComponent duplicateChildParent = null;
+
     while (foundChild != null)
     {
-      // If the parent matches, this is the one to move, rest are duplicates
+      // 2.a. If the parent matches, this could be the component that JSF-Runtime re-created
+      //  and added because it is in the physical document
       if (foundChild.getParent().equals(sourceParent))
       {
         movableChild = foundChild;
         movableChildIndex = sourceParent.getChildren().indexOf(movableChild);
       }
+      // 2.b.a. We could possibly find the child at its destination, because apart from
+      //  adding the change, the change was applied in previous request, and the move
+      //  could have been within the same naming container umbrella. In this case
+      //  we do not want to move anything and the movable child is considered as a 
+      //  duplicate and candidate for removal.
+      else if (foundChild.getParent().equals(destinationContainer))
+      {
+        isChildIdAtDestination = true;
+        movedChild = foundChild;
+        movedChildIndex = destinationContainer.getChildren().indexOf(movedChild);
+      }
+      // 2.c. Possible dup from subsequent MoveChildComponentChange in the sequence of multiple
+      //  moves of the component in this same request. For example, if the move is from A->B->C,
+      //  and if we are currently dealing with move from A->B, the component that was added at
+      //  position C (in addition to adding the move change to changemanager) will now be dup.
+      else
+      {
+        duplicateChild = foundChild;
+        duplicateChildIndex = foundChild.getParent().getChildren().indexOf(foundChild);
+        duplicateChildParent = foundChild.getParent();
+      }
 
       // Invariably, remove the found component from the tree. We remove the
       //  movableChild also, otherwise, findComponent blind loops on this same 
@@ -269,39 +304,74 @@ public final class MoveChildComponentCha
       //  NamingContainer.
       foundChild.getParent().getChildren().remove(foundChild);
 
-      // Try and find the next potential duplicate
+      // Try and find the next potential copy of the component to move
       foundChild = changeTargetComponent.findComponent(_movableChildScopedId);
     }
     
+    //  We need to re-attach the dup for now, the dupes will be eliminated gradually while applying
+    //  the successive move change involving the same component.
+    if (duplicateChild != null)
+    {
+      duplicateChildParent.getChildren().add(duplicateChildIndex, duplicateChild);
+    }
+
+    // Can't do anything without a movable child.    
     if(movableChild == null)
     {
       _LOG.warning("MOVABLE_CHILD_NOT_FOUND", _movableChildScopedId);
+      // Reverse any damage that we might have caused, and exit
+      if (movedChild != null)
+      {
+        destinationContainer.getChildren().add(movedChildIndex, movedChild);
+      }
       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())
+    // 2.b.b. Similar to situation in step #2.b.a, but here the move is across different naming 
+    //  containers, we could not catch this earlier.
+    if (!isChildIdAtDestination)
     {
-      if (movableChildId.equals(childComponent.getId()))
+      String movableChildId = movableChild.getId();
+      for (UIComponent childComponent:destinationContainer.getChildren())
+      {
+        if (movableChildId.equals(childComponent.getId()))
+        {
+          isChildIdAtDestination = true;
+          movedChild = childComponent;
+          // Temporarily remove this child, we might add it back in step #3 below.
+          movedChild.getParent().getChildren().remove(movedChild);
+          break;
+        }
+      }
+    }
+
+    // 3. Check whether the destination container has a child with same id.
+    if (isChildIdAtDestination)
+    {
+      _LOG.warning("MOVABLE_CHILD_SAME_ID_FOUND", _movableChildScopedId);
+
+      // 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
+      if ( (movableChild.getFamily().equals(movedChild.getFamily())) &&
+             (movableChild.getRendererType().equals(movedChild.getRendererType())) )
+      {
+        // Add back the moved child that we removed earlier.
+        destinationContainer.getChildren().add(movedChildIndex, movedChild);
+      }
+      else
       {
-        indexOfChildWithSameIdAtDestination = 
-          destinationContainer.getChildren().indexOf(childComponent);
-        childWithSameIdAtDestination = childComponent;
-        destinationContainer.getChildren().remove(childComponent);
+        // Duplicate child by id, but not of the same component type - a condition we cannot handle.
+        // Reverse any damage that we might have caused and exit
+        sourceParent.getChildren().add(movableChildIndex, movableChild);
       }
+      return;
     }
+
+    // We are now dealing with case where there were no duplicates, and a proper point-to-point
+    //  move should happen. Reattach the moveable child, so that move happens atomically at the end.
+    sourceParent.getChildren().add(movableChildIndex, movableChild);
     
-    // 4. See if we can find the insertBeforeComponent among the 
-    //  destinationContainer's children
+    // 4. See if we can find the insertBeforeComponent among the destinationContainer's children
     int insertIndex = -1;
     if (_insertBeforeId != null)
     {
@@ -315,16 +385,9 @@ public final class MoveChildComponentCha
         }
       }
   
-      // 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.
+      // insertBeforeId was specified, but we cannot find the insertBefore component. Exit.
       if (insertIndex == -1)
       {
-        if (childWithSameIdAtDestination != null)
-          destinationContainer.getChildren().add(
-            indexOfChildWithSameIdAtDestination,
-            childWithSameIdAtDestination);
-        
         _LOG.warning("INSERT_BEFORE_NOT_FOUND", _insertBeforeId);
         return;
       }

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java?rev=955269&r1=955268&r2=955269&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SessionChangeManager.java Wed Jun 16 15:27:21 2010
@@ -36,19 +36,25 @@ import javax.faces.component.UIComponent
 import javax.faces.component.UIViewRoot;
 import javax.faces.context.FacesContext;
 
+import org.apache.myfaces.trinidad.context.RequestContext;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.util.CollectionUtils;
 import org.apache.myfaces.trinidad.util.ComponentUtils;
 
+import org.apache.myfaces.trinidad.webapp.UIXComponentELTag;
+
 import org.w3c.dom.Document;
 
 
 /**
- * A ChangeManager implementation that manages
- *  persisting the added Changes at the session. This means
- *  the lifetime of Changes added such is within the session scope.
+ * A ChangeManager implementation that manages persisting the added Changes at the session. 
+ * This means the lifetime of Changes added such is within the session scope. If any of the changes
+ * are managed as state changes and restored by JSF state saving mechanism, the SessionChangeManager
+ * will not re-apply such changes. For example: AttributeComponentChanges are not applied during
+ * a postback unless its target component happened to be a result of any move/add operation, this is
+ * because attribute changes are handled by state manager during postback for common cases.
  * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/change/SessionChangeManager.java#0 $) $Date: 10-nov-2005.19:06:35 $
- */
+*/
 public class SessionChangeManager extends BaseChangeManager
 {
   /**
@@ -80,7 +86,7 @@ public class SessionChangeManager extend
           "INVALID_TYPE", root));
       }
       
-      rootId = ComponentUtils.getScopedIdForComponent((UIComponent)root, null);
+      rootId = ComponentUtils.getScopedIdForComponent((UIComponent)root, facesContext.getViewRoot());
     }
 
     _applyComponentChanges(facesContext, rootId);
@@ -108,7 +114,7 @@ public class SessionChangeManager extend
     // get the absolute scopedId for the target component so that we have a unique identifier
     // to compare
     String scopedIdForTargetComponent = 
-                                     ComponentUtils.getScopedIdForComponent(targetComponent, null);
+        ComponentUtils.getScopedIdForComponent(targetComponent, facesContext.getViewRoot());
 
     // try to collapse AttributeComponentChanges, handling component movement so that
     // we can collapse any attribute change on the same component
@@ -221,26 +227,137 @@ public class SessionChangeManager extend
       if (!_acceptChange(qualifiedChange, rootId))
         continue;
 
-      UIComponent targetComponent = 
-        viewRoot.findComponent(qualifiedChange.getTargetComponentScopedId());
+      String targetComponentScopedId = qualifiedChange.getTargetComponentScopedId();
+
+      UIComponent targetComponent = viewRoot.findComponent(targetComponentScopedId);
       
-      // Possible that the target component no more exists in the view
-      if (targetComponent != null)
-      {
-        ComponentChange componentChange = qualifiedChange.getComponentChange();
-        componentChange.changeComponent(targetComponent);
-      }
-      else
+      // Possible that the target component no more exists in the view, if yes, skip
+      if (targetComponent == null)
       {
         _LOG.info(this.getClass().getName(),
                   "applyComponentChangesForCurrentView",
                   "TARGET_COMPONENT_MISSING_CHANGE_FAILED",
-                  qualifiedChange.getTargetComponentScopedId());
+                  targetComponentScopedId);
+        continue;
+      }
+
+      ComponentChange componentChange = qualifiedChange.getComponentChange();
+
+      // We do not apply attribute changes if it is a postback, because we expect that
+      // 1. Users calling ChangeManager.addComponentChange() would also apply the change right at
+      //  that point in time (maybe by calling ComponentChange.changeComponent() method).
+      // 2. If #1 was done, JSF state manager will consider this a state change and will store and
+      //  restore it during subsequent postbacks, so there is no need for applying attribute changes
+      //  for postback cases. There are few exceptions where the state management will not help, for
+      //  which we force the attribute changes even when it is a postback.
+      if ( componentChange instanceof AttributeComponentChange &&
+           _isStateRestored(facesContext) &&
+           !_isAttributeChangeForced(facesContext, targetComponentScopedId) )
+            continue;
+
+      // Apply the change
+      componentChange.changeComponent(targetComponent);
+      
+      // Now that the change is applied, we can identify if the components altered by the currently
+      //  applied change needs forced application of any further changes regardless of request 
+      //  being a postback.
+      if (componentChange instanceof MoveChildComponentChange)
+      {
+        String destinationScopedId = 
+          ((MoveChildComponentChange)componentChange).getDestinationScopedId();
+        _addAttributeChangeForced(facesContext, destinationScopedId);
+      }
+      else if (componentChange instanceof SetFacetChildComponentChange)
+      {
+        String facetName = ((SetFacetChildComponentChange)componentChange).getFacetName();
+        UIComponent facetComponent = targetComponent.getFacet(facetName);
+        String facetScopedId = 
+          ComponentUtils.getScopedIdForComponent(facetComponent, facesContext.getViewRoot());
+        if (facetComponent != null)
+          _addAttributeChangeForced(facesContext, facetScopedId);
+      }
+      else if (componentChange instanceof AddChildComponentChange)
+      {
+        // Get the added component from AddComponentChange, this component is actually re-created 
+        //  from the proxy, and not the actual added component. 
+        //  Bit hacky but this is only way to get Id.
+        String addedComponentId = ((AddChildComponentChange)componentChange).getComponent().getId();
+        // Now get the actual added component finding from the parent to which it was added to
+        UIComponent addedComponent = 
+          ComponentUtils.findRelativeComponent(targetComponent, addedComponentId);
+        String addedChildComponentScopedId = 
+          ComponentUtils.getScopedIdForComponent(addedComponent, facesContext.getViewRoot());
+        if (addedComponent != null)
+          _addAttributeChangeForced(facesContext, addedChildComponentScopedId);
       }
     }    
   }
 
   /**
+   * Is the state restored by JSF state manager in this request. This is usually true if this is a
+   *  postback request. Additionally check if the document tag created a document component, because
+   *  if this is the case, we are sure that there was no state restoration.
+   */
+  private boolean _isStateRestored(FacesContext facesContext)
+  {
+    boolean docCompCreated = Boolean.TRUE.equals(facesContext.getExternalContext().
+                                   getRequestMap().get(UIXComponentELTag.DOCUMENT_CREATED_KEY));
+    return (docCompCreated) ? false : RequestContext.getCurrentInstance().isPostback();
+  }
+
+  /**
+   * For a given scopedId records that any further AttributeComponentChanges must be applied always.
+   */
+  private void _addAttributeChangeForced(FacesContext facesContext, String targetScopedId)
+  {
+    if (targetScopedId == null)
+      return;
+    
+    Object session = facesContext.getExternalContext().getSession(true);
+    
+    synchronized(session)
+    {
+      Map<String, Object> sessMap = facesContext.getExternalContext().getSessionMap();
+      
+      CopyOnWriteArrayList<String> forceChangesTargetList = (CopyOnWriteArrayList<String>)
+                                       sessMap.get(_ATTRIBUTE_CHANGE_FORCED_COMPONENTS);
+      
+      if (forceChangesTargetList == null)
+      {
+        forceChangesTargetList = new CopyOnWriteArrayList<String>();
+        sessMap.put(_ATTRIBUTE_CHANGE_FORCED_COMPONENTS, forceChangesTargetList);
+      }
+      
+      forceChangesTargetList.addIfAbsent(targetScopedId);
+    }
+  }
+  
+  /**
+   * Given a scopedId for the component, returns whether all AttributeComponentChange should be 
+   * forced on this component.
+   */
+  private boolean _isAttributeChangeForced(FacesContext facesContext, String targetScopedId)
+  {
+    if (targetScopedId == null)
+      return false;
+    
+    Object session = facesContext.getExternalContext().getSession(true);
+    
+    synchronized(session)
+    {
+      Map<String, Object> sessMap = facesContext.getExternalContext().getSessionMap();
+      
+      List<String> forceChangesTargetList = (List<String>)
+                                       sessMap.get(_ATTRIBUTE_CHANGE_FORCED_COMPONENTS);
+      
+      if (forceChangesTargetList == null || forceChangesTargetList.isEmpty())
+        return false;
+
+      return forceChangesTargetList.contains(targetScopedId);
+    }
+  }
+
+  /**
    * Tests whether the specified change should be applied based on the
    * specified root id.  If root id is null, all changes are accepted/applied.
    * If the root id is non-null, only changes which target ids underneath
@@ -461,6 +578,9 @@ public class SessionChangeManager extend
   private static final String _COMPONENT_CHANGES_MAP_FOR_SESSION_KEY =
     "org.apache.myfaces.trinidadinternal.ComponentChangesMapForSession";
     
+  private static final String _ATTRIBUTE_CHANGE_FORCED_COMPONENTS =
+    "org.apache.myfaces.trinidadinternal.AttributeChangeForcedComponents";
+    
   private static final TrinidadLogger _LOG = 
     TrinidadLogger.createTrinidadLogger(SessionChangeManager.class);
 }

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SetFacetChildComponentChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SetFacetChildComponentChange.java?rev=955269&r1=955268&r2=955269&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SetFacetChildComponentChange.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/SetFacetChildComponentChange.java Wed Jun 16 15:27:21 2010
@@ -19,13 +19,14 @@
 package org.apache.myfaces.trinidad.change;
 
 import javax.faces.component.UIComponent;
+
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
+
 /**
  * Change specialization for adding a facet.
- * While applying this Change, the specified component is re-created and added
- *  as a facet. If there were to be a facet by the specified name already, it 
- *  will be replaced.
+ * While applying this Change, the specified component is re-created and added as a facet. If there 
+ * were to be a facet by the specified name already, the new facet will not be added.
  * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/change/SetFacetChildComponentChange.java#0 $) $Date: 10-nov-2005.19:10:02 $
  */
 public class SetFacetChildComponentChange extends AddComponentChange 
@@ -72,8 +73,16 @@ public class SetFacetChildComponentChang
     
     if (facetComponent == null)
       return;
-      
+    
+    // Do not replace a facet if it already exists.
+    if (uiComponent.getFacets().get(_facetName) != null)
+    {
+      _LOG.info("FACET_ALREADY_PRESENT", _facetName);
+      return;
+    }
+
     uiComponent.getFacets().put(_facetName, facetComponent);
+
   }
   
   private final String _facetName;

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java?rev=955269&r1=955268&r2=955269&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UIXComponentELTag.java Wed Jun 16 15:27:21 2010
@@ -35,6 +35,7 @@ import javax.el.ValueExpression;
 
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
 import javax.faces.webapp.UIComponentELTag;
 
@@ -86,6 +87,12 @@ abstract public class UIXComponentELTag 
     //  created. End of document tag is a best bet.
     if (component instanceof UIXDocument)
     {
+      if (getCreated()) 
+      {
+        ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
+        // Used by SessionChangeManager to confirm that the state was not restored.
+        ec.getRequestMap().put(DOCUMENT_CREATED_KEY, Boolean.TRUE);
+      }
       ChangeManager cm = RequestContext.getCurrentInstance().getChangeManager();
       cm.applyComponentChangesForCurrentView(FacesContext.getCurrentInstance());
     }
@@ -488,6 +495,9 @@ abstract public class UIXComponentELTag 
     return sdf;
   }
 
+  public static final String DOCUMENT_CREATED_KEY = "org.apache.myfaces.trinidad.DOCUMENTCREATED";
+
   private MethodExpression  _attributeChangeListener;
   private String            _validationError;
+  
 }

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts?rev=955269&r1=955268&r2=955269&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts Wed Jun 16 15:27:21 2010
@@ -178,7 +178,7 @@
  <resource key="INVALID_INDEX">Invalid index</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>
+ <resource key="ATTEMPT_ADD_CHILD_WITH_DUPLICATE_ID">Change manager failed to apply AddChildComponentChange because a child component with the same id already exists. {0}</resource>
  
  <!-- NO_NODE_SPECIFIED -->
  <resource key="NO_NODE_SPECIFIED">No node specified</resource>
@@ -225,6 +225,9 @@
  <!-- FACET_NAME_MUST_SPECIFIED -->
  <resource key="FACET_NAME_MUST_SPECIFIED">Facet name must be specified</resource>
  
+ <!-- FACET_ALREADY_PRESENT -->
+ <resource key="FACET_ALREADY_PRESENT">Change manager failed to apply SetFacetChildComponentChange because a facet with the same name already exists. {0}</resource>
+
  <!-- MOVABLE_CHILD_REQUIRED -->
  <resource key="MOVABLE_CHILD_REQUIRED">Child component to be moved is required.</resource>
 
@@ -243,6 +246,9 @@
  <!-- DESTINATION_CONTAINER_NOT_FOUND -->
  <resource key="DESTINATION_CONTAINER_NOT_FOUND">Destination container could not be found: {0}.</resource>
 
+ <!-- MOVABLE_CHILD_SAME_ID_FOUND -->
+ <resource key="MOVABLE_CHILD_SAME_ID_FOUND">A different component with same id was found in the destination container. Move operation aborted: {0}.</resource>
+
  <!-- INSERT_BEFORE_NOT_FOUND -->
  <resource key="INSERT_BEFORE_NOT_FOUND">Insert before component or node could not be found: {0}.</resource>