You are viewing a plain text version of this content. The canonical link for it is here.
Posted to adffaces-commits@incubator.apache.org by aw...@apache.org on 2006/10/18 00:02:16 UTC

svn commit: r465103 - in /incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad: component/UIXCollection.java component/ValueMap.java render/ClientRowKeyManager.java render/ClientRowKeyManagerFactory.java

Author: awiner
Date: Tue Oct 17 17:02:15 2006
New Revision: 465103

URL: http://svn.apache.org/viewvc?view=rev&rev=465103
Log:
ADFFACES-210: move rowKey string token map from UIXCollection into corresponding Renderer.  Patch from Arjuna Wijeyekoon

Added:
    incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManager.java
    incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManagerFactory.java
Modified:
    incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java
    incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/ValueMap.java

Modified: incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java?view=diff&rev=465103&r1=465102&r2=465103
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/UIXCollection.java Tue Oct 17 17:02:15 2006
@@ -16,8 +16,8 @@
 package org.apache.myfaces.trinidad.component;
 
 import java.io.IOException;
-
 import java.io.Serializable;
+
 import java.util.AbstractMap;
 import java.util.Collections;
 import java.util.List;
@@ -30,11 +30,15 @@
 import javax.faces.event.AbortProcessingException;
 import javax.faces.event.FacesEvent;
 import javax.faces.event.PhaseId;
+import javax.faces.render.Renderer;
 
 import org.apache.myfaces.trinidad.event.SelectionEvent;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.model.CollectionModel;
 import org.apache.myfaces.trinidad.model.SortCriterion;
+import org.apache.myfaces.trinidad.render.ClientRowKeyManager;
+import org.apache.myfaces.trinidad.render.ClientRowKeyManagerFactory;
+
 
 /**
  * Base class for components that do stamping.
@@ -212,8 +216,7 @@
     // _stampState is stored as an instance variable, so it isn't
     // automatically saved
     Object superState = super.saveState(context);
-    Object stampState;
-    ValueMap<Object> currencyCache;
+    final Object stampState, clientKeyMgr;
 
     // becareful not to create the internal state too early:
     // otherwise, the internal state will be shared between
@@ -222,18 +225,16 @@
     if (iState != null)
     {
       stampState = iState._stampState;
-      currencyCache = iState._currencyCache;
-      if ((currencyCache != null) && currencyCache.isEmpty())
-        currencyCache = null;
+      clientKeyMgr = iState._clientKeyMgr;
     }
     else
     {
       stampState = null;
-      currencyCache = null;
+      clientKeyMgr = null;
     }
 
-    if ((superState != null) || (stampState != null) || (currencyCache != null))
-      return new Object[]{superState, stampState, currencyCache};
+    if ((superState != null) || (stampState != null) || (clientKeyMgr != null))
+      return new Object[]{superState, stampState, clientKeyMgr};
     return null;
   }
 
@@ -242,25 +243,25 @@
   @Override
   public void restoreState(FacesContext context, Object state)
   {
-    final Object superState, stampState, currencyCache;
+    final Object superState, stampState, clientKeyMgr;
     Object[] array = (Object[]) state;
     if (array != null)
     {
       superState = array[0];
       stampState = array[1];
-      currencyCache = array[2];
+      clientKeyMgr = array[2];
     }
     else
     {
-      superState = stampState = currencyCache = null;
+      superState = stampState = clientKeyMgr = null;
     }
     super.restoreState(context, superState);
 
-    if ((stampState != null) || (currencyCache != null))
+    if ((stampState != null) || (clientKeyMgr != null))
     {
       InternalState iState = _getInternalState(true);
       iState._stampState = (StampState) stampState;
-      iState._currencyCache = (ValueMap<Object>) currencyCache;
+      iState._clientKeyMgr = (ClientRowKeyManager) clientKeyMgr;
     }
     else
     {
@@ -270,7 +271,7 @@
       if (iState != null)
       {
         iState._stampState = null;
-        iState._currencyCache = null;
+        iState._clientKeyMgr = null;
       }
     }
   }
@@ -461,10 +462,14 @@
     InternalState istate = _getInternalState(true);
     // we must not clear the currency cache everytime. only clear
     // it in response to specific events: bug 4773659
+
+    // TODO all this code should be removed and moved into the renderer:
     if (istate._clearTokenCache)
     {
       istate._clearTokenCache = false;
-      _getCurrencyCache().clear();
+      ClientRowKeyManager keyMgr = getClientRowKeyManager();
+      if (keyMgr instanceof DefaultClientKeyManager)
+        ((DefaultClientKeyManager) keyMgr).clear();
     }
     _flushCachedModel();
 
@@ -531,57 +536,13 @@
     if (_equals(currencyObject, initKey))
       return null;
 
-    String key = _getToken(currencyObject);
+    FacesContext fc = FacesContext.getCurrentInstance();
+    String key = getClientRowKeyManager().getClientRowKey(fc, this, currencyObject);
     return key;
   }
 
-  private String _getToken(Object rowKey)
-  {
-    assert rowKey != null;
-
-    ValueMap<Object> currencyCache = _getCurrencyCache();
-    String key = (String) currencyCache.get(rowKey);
-    if (key == null)
-    {
-      if (rowKey instanceof String)
-      {
-        // TODO: make sure that this string is suitable for use as
-        // NamingContainer ids:
-        key = rowKey.toString();
-        if (_isOptimizedKey(key))
-        {
-          // no need to add to the token map:
-          return key;
-        }
-      }
-
-      key = _createToken(currencyCache);
 
-      if (_LOG.isFiner())
-        _LOG.finer("Storing token:"+key+
-                   " for rowKey:"+rowKey);
 
-      currencyCache.put(rowKey, key);
-    }
-    return key;
-  }
-
-  private boolean _isOptimizedKey(String key)
-  {
-    // if a key could be a number, then it might conflict with our
-    // internal representation of tokens. Therefore, if a key could be
-    // a number, then use the token cache.
-    // if there is no way this key can be a number, then it can
-    // be treated as an optimized key and can bypass the token cache
-    // system:
-    return ((key.length() > 0) && (!Character.isDigit(key.charAt(0))));
-  }
-
-  private String _createToken(ValueMap<Object> currencyCache)
-  {
-    String key = String.valueOf(currencyCache.size());
-    return key;
-  }
 
   /**
    * This is a safe way of getting currency keys and not accidentally forcing
@@ -617,7 +578,7 @@
    * the start of the encode phase on the next request.
    * <P>
    * This method gets the corresponding currencyKey and passes it to
-   * {@link #setCurrencyKey}
+   * {@link #setRowKey}
    * @see #getCurrencyString
    */
   public void setCurrencyString(String currency)
@@ -628,15 +589,15 @@
       return;
     }
 
-    Object currencyObject = _isOptimizedKey(currency)
-      ? currency
-      : _getCurrencyCache().getKey(currency);
-    if (currencyObject == null)
+    FacesContext fc = FacesContext.getCurrentInstance();
+    Object rowkey = getClientRowKeyManager().getRowKey(fc, this, currency);
+
+    if (rowkey == null)
     {
       _LOG.severe("Could not restore currency for currencyString:"+currency);
     }
     else
-      setRowKey(currencyObject);
+      setRowKey(rowkey);
   }
 
   /**
@@ -883,6 +844,23 @@
     return getCollectionModel(true);
   }
 
+  public final ClientRowKeyManager getClientRowKeyManager()
+  {
+    // this method must be public, because specific renderers
+    // need access to the ClientRowKeyManager so that they might prune it.
+    
+    InternalState iState = _getInternalState(true);
+    if (iState._clientKeyMgr == null)
+    {
+      FacesContext fc = FacesContext.getCurrentInstance();
+      Renderer r = getRenderer(fc);
+      iState._clientKeyMgr = (r instanceof ClientRowKeyManagerFactory)
+        ? ((ClientRowKeyManagerFactory) r).createClientRowKeyManager(fc, this)
+        : new DefaultClientKeyManager();
+    }
+    return iState._clientKeyMgr;
+  }
+
   /**
    * Gets the CollectionModel to use with this component.
    *
@@ -1129,14 +1107,6 @@
     return iState._stampState;
   }
 
-  private ValueMap<Object> _getCurrencyCache()
-  {
-    InternalState iState = _getInternalState(true);
-    if (iState._currencyCache == null)
-      iState._currencyCache = new ValueMap<Object>();
-    return iState._currencyCache;
-  }
-
   /**
    * sets an EL variable.
    * @param varName the name of the variable
@@ -1172,6 +1142,84 @@
     return component.getFacets().size();
   }
 
+  private static final class DefaultClientKeyManager extends ClientRowKeyManager
+  {
+
+    public void clear()
+    {
+      _currencyCache.clear();
+    }
+
+    public Object getRowKey(FacesContext context, UIComponent component, String clientRowKey)
+    {
+      if (_isOptimizedKey(clientRowKey))
+        return clientRowKey;
+      
+      ValueMap<Object,String> currencyCache = _currencyCache;
+      Object rowkey = currencyCache.getKey(clientRowKey);
+      return rowkey;
+    }
+
+    public String getClientRowKey(FacesContext context, UIComponent component, Object rowKey)
+    {
+      assert rowKey != null;
+
+      ValueMap<Object,String> currencyCache = _currencyCache;
+      String key = currencyCache.get(rowKey);
+      // check to see if we already have a string key:
+      if (key == null)
+      {
+        // we don't have a string-key, so create a new one.
+        
+        // first check to see if the rowkey itself can be used as the string-key:
+        if (rowKey instanceof String)
+        {
+          // TODO: make sure that this string is suitable for use as
+          // NamingContainer ids:
+          key = rowKey.toString();
+          if (_isOptimizedKey(key))
+          {
+            // no need to add to the token map:
+            return key;
+          }
+        }
+
+        key = _createToken(currencyCache);
+
+        if (_LOG.isFiner())
+          _LOG.finer("Storing token:"+key+
+                     " for rowKey:"+rowKey);
+
+        currencyCache.put(rowKey, key);
+      }
+      return key;
+    }
+
+    private static boolean _isOptimizedKey(String key)
+    {
+      // if a key could be a number, then it might conflict with our
+      // internal representation of tokens. Therefore, if a key could be
+      // a number, then use the token cache.
+      // if there is no way this key can be a number, then it can
+      // be treated as an optimized key and can bypass the token cache
+      // system:
+      return ((key.length() > 0) && (!Character.isDigit(key.charAt(0))));
+    }
+
+    private static String _createToken(ValueMap<Object,String> currencyCache)
+    {
+      String key = String.valueOf(currencyCache.size());
+      return key;
+    }
+
+    private ValueMap<Object,String> _currencyCache = new ValueMap<Object,String>();
+  }
+
+  // this component's internal state is stored in an inner class
+  // rather than in individual fields, because we want to make it
+  // 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 transient boolean _hasEvent = false;
@@ -1190,8 +1238,8 @@
     // this is the rowKey used to retrieve the default stamp-state for all rows:
     private transient Object _initialStampStateKey = _NULL;
 
+    private ClientRowKeyManager _clientKeyMgr = null;
     private StampState _stampState = null;
-    private ValueMap<Object> _currencyCache = null;
   }
 
   // do not assign a non-null value. values should be assigned lazily. this is

Modified: incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/ValueMap.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/ValueMap.java?view=diff&rev=465103&r1=465102&r2=465103
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/ValueMap.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/component/ValueMap.java Tue Oct 17 17:02:15 2006
@@ -34,18 +34,25 @@
  * 
  * @author The Oracle ADF Faces Team
  */
-final class ValueMap<T> extends AbstractMap<T, T> implements Externalizable 
+final class ValueMap<K, V> extends AbstractMap<K, V> implements Externalizable 
 {
   public ValueMap()
   {
-    // noarg constructor needed for serialization
+    // noarg constructor needed by the Externalizable interface
+    this(13);
+  }
+
+  public ValueMap(int initialCapacity)
+  {
+    _cache = new HashMap<K, V>(initialCapacity);
+    _valueMap = new HashMap<V, K>(initialCapacity);
   }
 
   /**
    * Gets the value associated with the given key
    */
   @Override
-  public T get(Object key)
+  public V get(Object key)
   {
     return _cache.get(key);
   }
@@ -53,19 +60,19 @@
   /**
    * Gets the key associated with the given value
    */
-  public Object getKey(Object value)
+  public K getKey(V value)
   {
     return _valueMap.get(value);
   }
   
   @Override
-  public T put(T key, T value)
+  public V put(K key, V value)
   {
-    T oldKey = _valueMap.put(value, key);
+    K oldKey = _valueMap.put(value, key);
     assert oldKey == null : "value:"+value+" is referenced by both key:"+key+
       " and key:"+oldKey;                          
 
-    T old = _cache.put(key, value);
+    V old = _cache.put(key, value);
     assert old == null : "can't put the same key twice";
     return old;
   }
@@ -84,17 +91,17 @@
   }
   
   @Override
-  public Set<Map.Entry<T, T>> entrySet()
+  public Set<Map.Entry<K, V>> entrySet()
   {
     return Collections.unmodifiableSet(_cache.entrySet());
   }
   
-  private static <T> Map<T, T> _setupValueMap(Map<T, T> cache)
+  private static <K,V> Map<V, K> _invertMap(Map<K, V> cache)
   {
-    Map<T, T> valueMap = new HashMap<T, T>(cache.size());
-    for(Map.Entry<T, T> entry : cache.entrySet())
+    Map<V, K> valueMap = new HashMap<V, K>(cache.size());
+    for(Map.Entry<K, V> entry : cache.entrySet())
     {
-      T old = valueMap.put(entry.getValue(), entry.getKey());
+      K old = valueMap.put(entry.getValue(), entry.getKey());
       assert old == null : "the value:"+entry.getValue()+
                            " was bound to both key:"+old+
                            " and key:"+entry.getKey();
@@ -105,18 +112,24 @@
   
   public void writeExternal(ObjectOutput out) throws IOException
   {
-    out.writeObject(_cache);
+    if (_cache.isEmpty())
+      out.writeObject(null);
+    else
+      out.writeObject(_cache);
   }
 
-  @SuppressWarnings("unchecked")
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
   {
-    _cache = (Map<T, T>) in.readObject();
-    _valueMap = _setupValueMap(_cache);
+    Map<K, V> cache = (Map<K, V>) in.readObject();
+    if (cache != null)
+    {
+      _cache = cache;
+      _valueMap = _invertMap(_cache);
+    }
   }
 
-  private Map<T, T> _cache = new HashMap<T, T>(13);
-  private transient Map<T, T> _valueMap = new HashMap<T, T>(13);
+  private Map<K, V> _cache;
+  private transient Map<V, K> _valueMap;
 
   //private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(InvertibleMap.class);
 }

Added: incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManager.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManager.java?view=auto&rev=465103
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManager.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManager.java Tue Oct 17 17:02:15 2006
@@ -0,0 +1,37 @@
+package org.apache.myfaces.trinidad.render;
+
+import java.io.Serializable;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * This class manages server-side
+ * rowkey Objects with client-side string keys.
+ * This class must be Serializable as it is state-saved along with the
+ * UIComponent state.
+ */
+public abstract class ClientRowKeyManager implements Serializable
+{
+  /**
+   * Gets a string version of a key that identifies the row with the given rowkey. 
+   * This string key can be used on the client-side to identify the row.
+   * If a string key for the given rowkey does not exist, then a new one is
+   * created. The lifespan of this string rowkey is entirely upto each
+   * implementation. Implementors must ensure that if a particular row is still
+   * present on the client-side, then its string key must also continue to be valid.
+   * @param rowKey the rowkey to convert into a client key. Note that
+   * null is special and is not allowed.
+   */
+  public abstract String getClientRowKey(FacesContext context, UIComponent component, Object rowKey);
+
+  /**
+   * Gets the corresponding server-side rowkey object from the given client-side string
+   * key. If the string key has expired, implementors should return null. However,
+   * if any part of a row is still present on the client-side, its corresponding
+   * string-key may not expire.
+   * @param clientRowKey the string key
+   * @return null, if the string key has expired, or never existed.
+   */
+  public abstract Object getRowKey(FacesContext context, UIComponent component, String clientRowKey);
+}

Added: incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManagerFactory.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManagerFactory.java?view=auto&rev=465103
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManagerFactory.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/render/ClientRowKeyManagerFactory.java Tue Oct 17 17:02:15 2006
@@ -0,0 +1,22 @@
+package org.apache.myfaces.trinidad.render;
+
+import javax.faces.component.UIComponent;
+
+import javax.faces.context.FacesContext;
+
+
+/**
+ * A producer of ClientRowKeyManagers.
+ * Typically this interface will be implemented by Renderers of stamping 
+ * components (like UIXCollection subclasses) that need to provide
+ * string-row-keys that can be used to identify data rows on the client.
+ */
+public interface ClientRowKeyManagerFactory
+{
+  /**
+   * Create a new ClientRowKeyManager for the given UIComponent
+   */
+  public ClientRowKeyManager createClientRowKeyManager(
+    FacesContext context, 
+    UIComponent component);
+}