Return-Path: X-Original-To: apmail-myfaces-commits-archive@www.apache.org Delivered-To: apmail-myfaces-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 39165EBB1 for ; Wed, 23 Jan 2013 23:15:52 +0000 (UTC) Received: (qmail 37487 invoked by uid 500); 23 Jan 2013 23:15:52 -0000 Delivered-To: apmail-myfaces-commits-archive@myfaces.apache.org Received: (qmail 37385 invoked by uid 500); 23 Jan 2013 23:15:52 -0000 Mailing-List: contact commits-help@myfaces.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "MyFaces Development" Delivered-To: mailing list commits@myfaces.apache.org Received: (qmail 37370 invoked by uid 99); 23 Jan 2013 23:15:52 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 23 Jan 2013 23:15:52 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 23 Jan 2013 23:15:48 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id B81A823889DE; Wed, 23 Jan 2013 23:15:27 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1437795 - /myfaces/trinidad/branches/arobinson74_jira1940/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java Date: Wed, 23 Jan 2013 23:15:27 -0000 To: commits@myfaces.apache.org From: arobinson74@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130123231527.B81A823889DE@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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. *

- *

- * 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. - *

- * */ 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 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 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 _getIterationStatusMap( Map 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(); 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 q = _getCurrentQueue(true); + Deque 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, Long> map = _getIterationIdStackToIdMap(true); + Map, Long> map = _getIterationIdStackToIdMap(true); - Deque q = _getCurrentQueue(true); - q.offerLast(iterationId); - List list = new ArrayList(q); + Deque q = _getCurrentQueue(true); + q.offerLast(iterationState); + List list = new ArrayList(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 q = _getCurrentQueue(false); + Deque q = _getCurrentQueue(false); if (!q.isEmpty()) { @@ -1322,15 +1382,15 @@ public class ForEachTag } Set unusedIds = new HashSet(); - Map, Long> map = _getIterationIdStackToIdMap(false); + Map, Long> map = _getIterationIdStackToIdMap(false); - for (Iterator, Long>> iter = map.entrySet().iterator(); + for (Iterator, Long>> iter = map.entrySet().iterator(); iter.hasNext();) { - Map.Entry, Long> entry = iter.next(); + Map.Entry, Long> entry = iter.next(); - List list = entry.getKey(); - IterationId baseId = list.get(0); + List 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, 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, Long> _getIterationIdStackToIdMap( boolean create) { - Map, Long> map = (Map, Long>)_viewMap.get(_VIEW_MAP_KEY); + Map, Long> map = (Map, Long>) + _viewMap.get(_VIEW_MAP_KEY); if (map == null && create) { - map = new HashMap, Long>(); + map = new HashMap, 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 _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 _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(); + _queue = new ArrayDeque(); _viewMap.put(_CURRENT_STACK_KEY, _queue); } }