TRINIDAD-2480
Register Table's InternalState as RowKeyChangeListener to CollectionModel

thanks to Jing
diff --git a/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java b/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java
index f134d5a..c4c90ca 100644
--- a/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java
+++ b/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java
@@ -54,6 +54,8 @@
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.model.CollectionModel;
 import org.apache.myfaces.trinidad.model.LocalRowKeyIndex;
+import org.apache.myfaces.trinidad.model.RowKeyChangeEvent;
+import org.apache.myfaces.trinidad.model.RowKeyChangeListener;
 import org.apache.myfaces.trinidad.model.SortCriterion;
 import org.apache.myfaces.trinidad.render.ClientRowKeyManager;
 import org.apache.myfaces.trinidad.render.ClientRowKeyManagerFactory;
@@ -491,6 +493,7 @@
       // It is bad if the rowKey changes after _restoreStampState() and
       // before _saveStampState(). Therefore, we cache it:
       iState._currentRowKey = getCollectionModel().getRowKey();
+      iState._model.addRowKeyChangeListener(iState);
     }
 
     return iState._currentRowKey;
@@ -952,6 +955,9 @@
     InternalState iState = _getInternalState(true);
     // mark the cached rowKey as invalid:
     iState._currentRowKey = _NULL;
+    // we don't have cached rowkey, thus remove rowkeychangelistener
+    if (iState._model != null)
+      iState._model.removeRowKeyChangeListener(iState);
   }
 
   /**
@@ -1963,9 +1969,27 @@
     Object value = getValue();
     if (iState._value != value)
     {
+      CollectionModel oldModel = iState._model;
       iState._value = value;
       iState._model = createCollectionModel(iState._model, value);
       postCreateCollectionModel(iState._model);
+
+      // if the underlying model is changed, we need to remove 
+      // the listener from the old model. And if we still have cached
+      // rowkey, we need to add the listener back to the new model.
+
+      if (oldModel != iState._model)
+      {
+        if (oldModel != null)
+        {
+          oldModel.removeRowKeyChangeListener(iState);
+        }
+
+        if (iState._currentRowKey != _NULL)
+        {
+          iState._model.addRowKeyChangeListener(iState);
+        }
+      }
     }
   }
 
@@ -2323,7 +2347,12 @@
     if (iState != null)
     {
       iState._value = null;
-      iState._model= null;
+
+      if (iState._model != null)
+      {
+        iState._model.removeRowKeyChangeListener(iState);
+        iState._model = null;
+      }
     }
   }
 
@@ -2405,7 +2434,7 @@
   // easy to quickly suck out or restore its internal state,
   // when this component is itself used as a stamp inside some other
   // stamping container, eg: nested tables.
-  private static final class InternalState implements Serializable
+  private static final class InternalState implements RowKeyChangeListener, Serializable
   {
     private transient boolean _hasEvent = false;
     private transient Object _prevVarValue = _NULL;
@@ -2433,6 +2462,35 @@
     // changes in the middle, we'll still be able to find the right stamp state.
     private Map<String, String> _idToIndexMap = null;
 
+    public void onRowKeyChange(RowKeyChangeEvent rowKeyChangeEvent)
+    {
+      Object newKey = rowKeyChangeEvent.getNewRowKey();
+      Object oldKey = rowKeyChangeEvent.getOldRowKey();
+
+      if (newKey != null && oldKey != null && !newKey.equals(oldKey))
+      {
+        // first replace the cached row key
+        if (oldKey.equals(_currentRowKey))
+          _currentRowKey = newKey;
+
+        // then update stamp state for the affected entries.
+        if (_stampState == null || _idToIndexMap == null)
+          return;
+
+        int stampCompCount = _idToIndexMap.size();
+
+        for (int index = 0; index < stampCompCount; index++)
+        {
+          String stampId = String.valueOf(index);
+          Object state = _stampState.get(oldKey, stampId);
+          if (state == null)
+            continue;
+          _stampState.put(oldKey, stampId, null);
+          _stampState.put(newKey, stampId, state);
+        }
+      }
+    }
+
     private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException
     {