You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ar...@apache.org on 2013/01/24 00:15:27 UTC

svn commit: r1437795 - /myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java

Author: arobinson74
Date: Wed Jan 23 23:15:27 2013
New Revision: 1437795

URL: http://svn.apache.org/viewvc?rev=1437795&view=rev
Log:
Add more comments and make the code a bit more readable

Modified:
    myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java

Modified: myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java?rev=1437795&r1=1437794&r2=1437795&view=diff
==============================================================================
--- myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java (original)
+++ myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java Wed Jan 23 23:15:27 2013
@@ -80,18 +80,13 @@ import org.apache.myfaces.trinidad.webap
  * The Trinidad for each tag overcomes limitations of the default forEach tag. This is due to
  * the standard for each tag using indexed value expressions stored in the variable mappers. This
  * usage causes issues if the collection that backs the for each is ever changed. In order to
- * address this, this Trinidad tag uses a level of indirection to store the information. The tag
- * uses something called and execution ID that represents each invocation of a for each tags'
- * doStartTag. This execution ID is used between requests to store a map of the keys of the
- * collection to tie to the iteration status data (exposed via varStatus).
+ * address this, this Trinidad tag uses a level of indirection to store the information. An
+ * iteration ID is generated that identifies a particular iteration of a for each tag, including
+ * nested tags. This ID is stored in the value expressions and is used to look up the current
+ * iteration status for that ID. The iteration ID is based on the scoped ID of the parent component,
+ * the JSP ID of the forEach tag, the key of the item from the collection and any parent for
+ * each iteration IDs.
  * </p>
- * <p>
- * This indirection is used my the value expressions. Instead of saving off the current var status
- * data of an item directly into the variable mapper, the execution ID and the key of the item is
- * stored into the value expression. This allows the for each tag to keep the iteration status
- * up to date for each value expression even when changes are made to the collection.
- * </p>
- *
  */
 public class ForEachTag
   extends TrinidadTagSupport
@@ -291,12 +286,7 @@ public class ForEachTag
       // We've finished iterating, we need to clean up by restore EL state and the variable mapper
       _restoreContextVariables(vm);
 
-      Set<Long> unusedIds = _iterationIdUtil.removeUnusedIds(_jspId,
-        _getParentComponentScopedId(FacesContext.getCurrentInstance()));
-      for (Long id : unusedIds)
-      {
-        _iterationStatusMap.remove(id);
-      }
+      _removeUnusedIterationIds();
 
       return SKIP_BODY;
     }
@@ -380,6 +370,13 @@ public class ForEachTag
     }
   }
 
+  /**
+   * Assigns an iteration ID to the current iteration of the for each tag. Called from both
+   * doStartTag and doAfterBody when a new iteration is about to begin
+   * @param facesContext the faces context
+   * @param calledFromDoStartTag true if called from do start tag so that the tag can start tracking
+   *        the processed iteration IDs to know which are no longer in use.
+   */
   private void _assignIterationId(
     FacesContext facesContext,
     boolean      calledFromDoStartTag)
@@ -391,14 +388,14 @@ public class ForEachTag
         facesContext.getExternalContext().getRequestMap());
     }
 
-    String scopedId = _getParentComponentScopedId(facesContext);
     if (calledFromDoStartTag)
     {
-      _iterationIdUtil.beginTag(_jspId, scopedId);
+      _iterationIdUtil.beginTag();
     }
 
     Serializable key = _currentIterationStatus.getKey();
-    IterationId iterIdObj = new IterationId(scopedId, _jspId, key);
+    String scopedId = _getParentComponentScopedId(facesContext);
+    IterationState iterIdObj = new IterationState(scopedId, _jspId, key);
     _iterationId = _iterationIdUtil.push(iterIdObj);
     if (_LOG.isFinest())
     {
@@ -509,6 +506,25 @@ public class ForEachTag
     }
   }
 
+  /**
+   * Called from doAfterBody once all iterations are done to remove the data tied to unused
+   * iteration IDs
+   */
+  private void _removeUnusedIterationIds()
+  {
+    Set<Long> unusedIds = _iterationIdUtil.removeUnusedIds(_jspId,
+      _getParentComponentScopedId(FacesContext.getCurrentInstance()));
+    for (Long id : unusedIds)
+    {
+      _iterationStatusMap.remove(id);
+    }
+  }
+
+  /**
+   * Get the scoped ID of the parent component of this tag
+   * @param facesContext the faces context
+   * @return the scoped ID or an empty string if the tag is not under a component
+   */
   private String _getParentComponentScopedId(
     FacesContext facesContext)
   {
@@ -595,6 +611,14 @@ public class ForEachTag
     _iterationStatusMap = _getIterationStatusMap(viewMap, true);
   }
 
+  /**
+   * Retrieve the map that stores the iteration status for a given iteration ID. This map is
+   * dynamically updated per request to ensure that the iteration status revealed in the var status
+   * is up to date.
+   * @param viewMap the view map
+   * @param create the map will be created if it does not exist if this is true
+   * @return the map
+   */
   private static Map<Long, IterationStatus> _getIterationStatusMap(
     Map<String, Object> viewMap,
     boolean             create)
@@ -604,6 +628,9 @@ public class ForEachTag
     {
       if (create)
       {
+        // The iteration status map is stored onto the view map so that we can still get the
+        // iteration status values from EL during the next request, before the JSP tags are
+        // processed once again.
         map = new HashMap<Long, IterationStatus>();
         viewMap.put(_ITERATION_MAP_KEY, map);
       }
@@ -1018,10 +1045,10 @@ public class ForEachTag
   /**
    * Class that provides the information to identify the iteration of a particular for each tag
    */
-  public final static class IterationId
+  public final static class IterationState
     implements Serializable
   {
-    public IterationId(
+    public IterationState(
       String       parentComponentScopedId,
       String       jspId,
       Serializable key)
@@ -1038,11 +1065,11 @@ public class ForEachTag
       {
         return true;
       }
-      if (!(object instanceof ForEachTag.IterationId))
+      if (!(object instanceof ForEachTag.IterationState))
       {
         return false;
       }
-      final ForEachTag.IterationId other = (ForEachTag.IterationId) object;
+      final IterationState other = (IterationState) object;
       if (!(_scopedId == null ? other._scopedId == null : _scopedId.equals(other._scopedId)))
       {
         return false;
@@ -1070,16 +1097,32 @@ public class ForEachTag
       return result;
     }
 
+    /**
+     * Get the scoped ID of the parent component of the for each tag. This is used in conjunction
+     * with the JSP ID since there is nothing preventing the JSP container from using the same
+     * JSP ID in different included files processed during the same request, it is only guaranteed
+     * to be unique to the current page.
+     * @return the scoped ID
+     */
     public String getScopedId()
     {
       return _scopedId;
     }
 
+    /**
+     * The JSP ID of the for each tag
+     * @return JSP ID
+     */
     public String getJspId()
     {
       return _jspId;
     }
 
+    /**
+     * The key of the item from the items collection. If a indexed based collection, or if the
+     * items attribute was not given the key is just the index.
+     * @return the item's key
+     */
     public Serializable getKey()
     {
       return _key;
@@ -1092,7 +1135,7 @@ public class ForEachTag
                _scopedId, _jspId, _key);
     }
 
-    @SuppressWarnings("compatibility:7983047193389138908")
+    @SuppressWarnings("compatibility:-2690507119119928732")
     private static final long serialVersionUID = 1L;
 
     private final String _scopedId;
@@ -1240,6 +1283,10 @@ public class ForEachTag
     private final Serializable _key;
   }
 
+  /**
+   * Helper class to help keep track of the iteration IDs and storing the necessary values on the
+   * view map to maintain the tag state between requests to be able to track changes.
+   */
   private static class IterationIdUtil
   {
     private IterationIdUtil(
@@ -1250,16 +1297,22 @@ public class ForEachTag
       _reqMap = reqMap;
     }
 
-    IterationId pop()
+    /**
+     * Called during the doAfterBody to signify the end of one iteration
+     */
+    IterationState pop()
     {
       return _getCurrentQueue(false).removeLast();
     }
 
-    void beginTag(
-      String currentJspId,
-      String parentComponentScopedId)
+    /**
+     * Lets the class know that the doStartTag has been called on the for each tag. This method
+     * begins tracking the processed iteration IDs so that IDs that are not used during the
+     * current request can be removed from the view scope memory.
+     */
+    void beginTag()
     {
-      Deque<IterationId> q = _getCurrentQueue(true);
+      Deque<IterationState> q = _getCurrentQueue(true);
 
       if (q.isEmpty())
       {
@@ -1276,20 +1329,27 @@ public class ForEachTag
       }
     }
 
+    /**
+     * Called by doStartTag and doAfterBody to notify this class that a new iteration has begun.
+     * @param iterationState the object with the information required to be able to identify the
+     *        current iteration state of the calling for each tag
+     * @return an iteration ID
+     */
     Long push(
-      IterationId iterationId)
+      IterationState iterationState)
     {
-      Map<List<IterationId>, Long> map = _getIterationIdStackToIdMap(true);
+      Map<List<IterationState>, Long> map = _getIterationIdStackToIdMap(true);
 
-      Deque<IterationId> q = _getCurrentQueue(true);
-      q.offerLast(iterationId);
-      List<IterationId> list = new ArrayList<IterationId>(q);
+      Deque<IterationState> q = _getCurrentQueue(true);
+      q.offerLast(iterationState);
+      List<IterationState> list = new ArrayList<IterationState>(q);
 
       // See if this is already has an ID from a previous request
       Long id = map.get(list);
 
       if (id == null)
       {
+        // This is a new iteration, assign a new ID
         id = _getNextId().getAndIncrement();
         map.put(list, id);
       }
@@ -1311,7 +1371,7 @@ public class ForEachTag
       String    currentJspId,
       String    parentComponentScopedId)
     {
-      Deque<IterationId> q = _getCurrentQueue(false);
+      Deque<IterationState> q = _getCurrentQueue(false);
 
       if (!q.isEmpty())
       {
@@ -1322,15 +1382,15 @@ public class ForEachTag
       }
 
       Set<Long> unusedIds = new HashSet<Long>();
-      Map<List<IterationId>, Long> map = _getIterationIdStackToIdMap(false);
+      Map<List<IterationState>, Long> map = _getIterationIdStackToIdMap(false);
 
-      for (Iterator<Map.Entry<List<IterationId>, Long>> iter = map.entrySet().iterator();
+      for (Iterator<Map.Entry<List<IterationState>, Long>> iter = map.entrySet().iterator();
         iter.hasNext();)
       {
-        Map.Entry<List<IterationId>, Long> entry = iter.next();
+        Map.Entry<List<IterationState>, Long> entry = iter.next();
 
-        List<IterationId> list = entry.getKey();
-        IterationId baseId = list.get(0);
+        List<IterationState> list = entry.getKey();
+        IterationState baseId = list.get(0);
 
         // Only process the entries that are for this tag
         if (currentJspId.equals(baseId.getJspId()) &&
@@ -1347,7 +1407,7 @@ public class ForEachTag
             if (_LOG.isFinest())
             {
               StringBuilder path = new StringBuilder();
-              for (IterationId iterId : list)
+              for (IterationState iterId : list)
               {
                 path.append('\n').append("  ").append(iterId);
               }
@@ -1363,19 +1423,35 @@ public class ForEachTag
       return unusedIds;
     }
 
-    private Map<List<IterationId>, Long> _getIterationIdStackToIdMap(
+    /**
+     * Retrieve the map that is used to tie the current iteration stack to the iteration IDs.
+     * The keys of the map are the stack of iteration states to get to the current iteration.
+     * For nested for each tags the list will be longer than one.
+     *
+     * @param create if the map does not exist yet setting this to true will cause the map to be
+     *        created
+     * @return the map
+     */
+    private Map<List<IterationState>, Long> _getIterationIdStackToIdMap(
       boolean create)
     {
-      Map<List<IterationId>, Long> map = (Map<List<IterationId>, Long>)_viewMap.get(_VIEW_MAP_KEY);
+      Map<List<IterationState>, Long> map = (Map<List<IterationState>, Long>)
+        _viewMap.get(_VIEW_MAP_KEY);
       if (map == null && create)
       {
-        map = new HashMap<List<IterationId>, Long>();
+        map = new HashMap<List<IterationState>, Long>();
         _viewMap.put(_VIEW_MAP_KEY, map);
       }
 
       return map;
     }
 
+    /**
+     * By using an a atomic long instance, the value in the view map may be incremented instead
+     * of having to keep putting new Long instances into the view map. It is only used as a
+     * mutable long object and not for its concurrency properties.
+     * @return the value that contains the next ID to be used as an iteration ID
+     */
     private AtomicLong _getNextId()
     {
       if (_id == null)
@@ -1391,7 +1467,13 @@ public class ForEachTag
       return _id;
     }
 
-    private Deque<IterationId> _getCurrentQueue(
+    /**
+     * Gets the current queue of iteration state objects. This will be larger than one for nested
+     * for each tags.
+     * @param create if true the queue will be created if it does not exist
+     * @return the queue
+     */
+    private Deque<IterationState> _getCurrentQueue(
       boolean create)
     {
       if (_queue == null)
@@ -1399,7 +1481,7 @@ public class ForEachTag
         _queue = (Deque)_viewMap.get(_CURRENT_STACK_KEY);
         if (_queue == null && create)
         {
-          _queue = new ArrayDeque<IterationId>();
+          _queue = new ArrayDeque<IterationState>();
           _viewMap.put(_CURRENT_STACK_KEY, _queue);
         }
       }