You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by bs...@apache.org on 2011/04/14 03:30:40 UTC

svn commit: r1091984 - /myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/RowKeySetAttributeChange.java

Author: bsullivan
Date: Thu Apr 14 01:30:40 2011
New Revision: 1091984

URL: http://svn.apache.org/viewvc?rev=1091984&view=rev
Log:
TRINIDAD-2074 RowKeySetAttributeChange doesn't check if oldValue is non-null and a RowKeySet before casting and dereferencing 

Modified:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/RowKeySetAttributeChange.java

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/RowKeySetAttributeChange.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/RowKeySetAttributeChange.java?rev=1091984&r1=1091983&r2=1091984&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/RowKeySetAttributeChange.java (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/RowKeySetAttributeChange.java Thu Apr 14 01:30:40 2011
@@ -1,21 +1,3 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- * 
- *  http://www.apache.org/licenses/LICENSE-2.0
- * 
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
 package org.apache.myfaces.trinidad.change;
 
 import java.util.Map;
@@ -31,7 +13,15 @@ import org.apache.myfaces.trinidad.model
 
 /**
  * Handles RowKeySetAttribute changes, which need to be handled specially because they are mutable
- * and programmers assume that the instances don't change
+ * and programmers assume that the instances don't change.
+ * 
+ * Cases that we need to worry about:
+ * 1) old value is null
+ * 2) new value is null (should clear existing RowKeySet if possible)
+ * 3) old value = new value
+ * 4) new value is a ValueExpression or ValueBinding
+ * 5) old value is a ValueExpression that needs to be evaluated in context
+ * 6) RowKeySet is internally bound to a model that needs to be evaluated in context
  */
 public final class RowKeySetAttributeChange extends AttributeComponentChange
 {
@@ -47,144 +37,112 @@ public final class RowKeySetAttributeCha
 
   @Override
   @SuppressWarnings("deprecation")
-  public void changeComponent(UIComponent uiComponent)
+  public void changeComponent(UIComponent component)
   {
-    Map<String, Object> attributeMap = uiComponent.getAttributes();
+    Map<String, Object> attributeMap = component.getAttributes();
 
-    Object attributeValue = getAttributeValue();
-    String attributeName  = getAttributeName();
+    Object newAttributeValue = getAttributeValue();
+    String attrName  = getAttributeName();
     
-    // if the attributevalue is a ValueExpression or ValueBinding, use the
-    // appropriate setValueExpression/setValueBinding call and remove the
-    // current attribute value, if any, so that the ValueExpression/ValueBinding
-    // can take precedence
-    if (attributeValue instanceof ValueExpression)
+    if ((newAttributeValue instanceof RowKeySet) || (newAttributeValue == null))
     {
-      uiComponent.setValueExpression(attributeName, (ValueExpression)attributeValue);
-      attributeMap.remove(attributeName);
+      // Specially handle RowKeySet case by replacing the contents of the RowKeySet in-place
+      // rather than replacing the entire object.  This keeps the mutable object instance from
+      // changing
+      _updateRowKeySetInPlace(component, attrName, (RowKeySet)newAttributeValue);
     }
-    else if (attributeValue instanceof ValueBinding)
+    else if (newAttributeValue instanceof ValueExpression)
     {
-      uiComponent.setValueBinding(attributeName, (ValueBinding)attributeValue);
-      attributeMap.remove(attributeName);
+      // if the new attribute value is a ValueExpession, set it and remove the old value
+      // so that the ValueExpression takes precedence
+      component.setValueExpression(attrName, (ValueExpression)newAttributeValue);
+      attributeMap.remove(attrName);
+    }
+    else if (newAttributeValue instanceof ValueBinding)
+    {
+      // if the new attribute value is a ValueBinding, set it and remove the old value
+      // so that the ValueBinding takes precedence
+      component.setValueBinding(attrName, (ValueBinding)newAttributeValue);
+      attributeMap.remove(attrName);
     }
     else
-    {      
-      // Specially handle RowKeySet case by replacing the contents of the RowKeySet in-place
-      // rather than replacing the entire object.  This keeps the mutable object instance from
-      // changing
-      if (attributeValue instanceof RowKeySet)
-      {
-        ValueExpression expression = uiComponent.getValueExpression(attributeName);
-        final FacesContext context = FacesContext.getCurrentInstance();
-        
-        if (expression != null)
-        {
-          //use EL to get the oldValue and then determine whether we need to update in place
-          context.getViewRoot().invokeOnComponent(
-            context,
-            _clientId,
-            new GetOldValueAndUpdate(expression, (RowKeySet)attributeValue));
-        }
-        else
-        {
-          context.getViewRoot().invokeOnComponent(context, _clientId,
-                 new GetOldValueAndUpdate(attributeName, (RowKeySet)attributeValue));
-        
-        }       
-      }
-      
-      attributeMap.put(attributeName, attributeValue);
+    {
+      // perform the default behavior
+      attributeMap.put(attrName, newAttributeValue);
     }
   }
-  
-  private static void _updateKeySet(String clientId, RowKeySet oldKeySet, RowKeySet newKeySet)
+
+  private void _updateRowKeySetInPlace(UIComponent component, String attrName, RowKeySet newValue)
   {
-    // check for equality because otherwise we would clear ourselves and end up empty
-    if (oldKeySet != newKeySet)
-    {
-      // no client id, so we're in context
-      if (clientId == null)
-      {
-        oldKeySet.clear();
-        oldKeySet.addAll(newKeySet);        
-      }
-      else
-      {
-        final FacesContext context = FacesContext.getCurrentInstance();
-        
-        context.getViewRoot().invokeOnComponent(
-           context,
-           clientId,
-           new RowKeySetUpdater(oldKeySet, newKeySet));
-      }
-    }    
+    ValueExpression oldExpression = component.getValueExpression(attrName);
+    
+    // due to bug in how the trinidad table and tree handle their RowKeySets, always use
+    // invoke on component and get the old value in context all of the time for now rather
+    // than trying to get the value directly if we don't have an expression
+    //use EL to get the oldValue and then determine whether we need to update in place
+    final FacesContext context = FacesContext.getCurrentInstance();
+                
+    context.getViewRoot().invokeOnComponent(
+      context,
+      _clientId,
+      new GetOldValueAndUpdate(oldExpression, attrName, newValue));
   }
-  
+    
   /**
    * Get the oldValue in context and update it in context
    */
   private static final class GetOldValueAndUpdate implements ContextCallback
   {
-    public GetOldValueAndUpdate(ValueExpression expression, RowKeySet newKeySet)
+    public GetOldValueAndUpdate(ValueExpression expression, String attributeName, RowKeySet newKeySet)
     {
-      _attributeName = null;
       _expression = expression;
-      _newKeySet  = newKeySet;
-    }
-    public GetOldValueAndUpdate(String attributeName, RowKeySet newKeySet)
-    {
-      _expression = null;
       _attributeName = attributeName;
       _newKeySet  = newKeySet;
     }
-
+    
     public void invokeContextCallback(FacesContext context,
                                       UIComponent target)
     {
+      Object oldValue;
+      
+      // due to bug in how tables and trees handle RowKeySet, temporarily support getting the
+      // old value in context, even when we don't have a value expression
       if (_expression != null)
-      {
-        // update the KeySet with the old and new values
-        RowKeySetAttributeChange._updateKeySet(null,
-                                             (RowKeySet)_expression.getValue(context.getELContext()),
-                                             _newKeySet);
-      }
-      else 
-      {
-        Map<String, Object> attributeMap = target.getAttributes();
-        RowKeySet oldKeySet = (RowKeySet) attributeMap.get(_attributeName);
-
-        // update the KeySet with the old and new values
-        RowKeySetAttributeChange._updateKeySet(null,
-                                             oldKeySet,
-                                             _newKeySet);
-      }
-    }
-    
-    private final ValueExpression _expression;
-    private final String _attributeName;
-    private final RowKeySet _newKeySet;
-  }
-
-  /**
-   * Makes sure that we clear and add the RowKeySet in context
-   */
-  private static final class RowKeySetUpdater implements ContextCallback
-  {
-    public RowKeySetUpdater(RowKeySet oldKeySet, RowKeySet newKeySet)
-    {
-      _oldKeySet = oldKeySet;
-      _newKeySet = newKeySet;
+        oldValue = _expression.getValue(context.getELContext());
+      else
+        oldValue = target.getAttributes().get(_attributeName);
+      
+      // update the old KeySet with the old and new values
+      _updateKeySet(target, oldValue);
     }
 
-    public void invokeContextCallback(FacesContext context,
-                                      UIComponent target)
+    private void _updateKeySet(UIComponent component, Object oldValue)
     {
-      _oldKeySet.clear();
-      _oldKeySet.addAll(_newKeySet);
+      // check for equality because otherwise we would clear ourselves and end up empty
+      if (oldValue != _newKeySet)
+      {
+        // if the old value is a RowKeySet, we can replace in place
+        if (oldValue instanceof RowKeySet)
+        {
+          RowKeySet oldKeySet = (RowKeySet)oldValue;
+          
+          oldKeySet.clear();
+          
+          if (_newKeySet != null)
+          {
+            oldKeySet.addAll(_newKeySet);
+          }
+        }
+        else
+        {
+          // if the oldKeySet is null, just set the new keySet
+          component.getAttributes().put(_attributeName, _newKeySet);
+        }
+      }    
     }
     
-    private final RowKeySet _oldKeySet;
+    private final ValueExpression _expression;
+    private final String _attributeName;
     private final RowKeySet _newKeySet;
   }