You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2010/07/28 04:50:59 UTC

svn commit: r979940 - in /myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit: FullVisitContext.java MockVisitContextFactory.java PartialVisitContext.java

Author: lu4242
Date: Wed Jul 28 02:50:58 2010
New Revision: 979940

URL: http://svn.apache.org/viewvc?rev=979940&view=rev
Log:
MYFACESTEST-22 MockVisitContextFactory should behave like the myfaces core VisitContextFactoryImpl

Added:
    myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java
    myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java
Modified:
    myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java

Added: myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java
URL: http://svn.apache.org/viewvc/myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java?rev=979940&view=auto
==============================================================================
--- myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java (added)
+++ myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java Wed Jul 28 02:50:58 2010
@@ -0,0 +1,147 @@
+/*
+ * 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.test.mock.visit;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>A VisitContext implementation that is
+ * used when performing a full component tree visit.</p>
+ * 
+ * @author Werner Punz, Blake Sullivan (latest modification by $Author: matzew $)
+ * @version $Rev: 885739 $ $Date: 2009-12-01 06:27:24 -0500 (Mar, 01 Dic 2009) $
+ */
+public class FullVisitContext extends VisitContext
+{
+
+  /**
+   * Creates a FullVisitorContext instance.
+   * @param facesContext the FacesContext for the current request
+   * @throws NullPointerException  if {@code facesContext}
+   *                               is {@code null}
+   */    
+  public FullVisitContext(FacesContext facesContext)
+  {
+    this(facesContext, null);
+  }
+
+  /**
+   * Creates a FullVisitorContext instance with the specified
+   * hints.
+   *
+   * @param facesContext the FacesContext for the current request
+   * @param hints a the VisitHints for this visit
+   * @param phaseId PhaseId, if any that visit is ocurring under
+   * @throws NullPointerException  if {@code facesContext}
+   *                               is {@code null}
+   * @throws IllegalArgumentException if the phaseId is specified and
+   * hints does not contain VisitHint.EXECUTE_LIFECYCLE
+   */    
+  public FullVisitContext(
+    FacesContext facesContext,
+    Set<VisitHint> hints)
+  {
+    if (facesContext == null)
+      throw new NullPointerException();
+
+    _facesContext = facesContext;
+
+    // Copy and store hints - ensure unmodifiable and non-empty
+    EnumSet<VisitHint> hintsEnumSet = ((hints == null) || (hints.isEmpty()))
+                                          ? EnumSet.noneOf(VisitHint.class)
+                                          : EnumSet.copyOf(hints);
+
+    _hints = Collections.unmodifiableSet(hintsEnumSet);
+  }
+
+  /**
+   * @see VisitContext#getFacesContext VisitContext.getFacesContext()
+   */
+  @Override
+  public FacesContext getFacesContext()
+  {
+    return _facesContext;
+  }
+
+  /**
+   * @see VisitContext#getIdsToVisit VisitContext.getIdsToVisit()
+   */
+  @Override
+  public Collection<String> getIdsToVisit()
+  {
+    // We always visits all ids
+    return ALL_IDS;
+  }
+
+  /**
+   * @see VisitContext#getSubtreeIdsToVisit VisitContext.getSubtreeIdsToVisit()
+   */
+  @Override
+  public Collection<String> getSubtreeIdsToVisit(UIComponent component)
+  {
+    // Make sure component is a NamingContainer
+    if (!(component instanceof NamingContainer))
+    {
+      throw new IllegalArgumentException("Component is not a NamingContainer: " + component);
+    }
+
+    // We always visits all ids
+    return ALL_IDS;
+  }
+
+  /**
+   * @see VisitContext#getHints VisitContext.getHints
+   */
+  @Override
+  public Set<VisitHint> getHints()
+  {
+    return _hints;
+  }
+
+  /**
+   * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback()
+   */
+  @Override
+  public VisitResult invokeVisitCallback(
+    UIComponent component, 
+    VisitCallback callback)
+  {
+    // Nothing interesting here - just invoke the callback.
+    // (PartialVisitContext.invokeVisitCallback() does all of the 
+    // interesting work.)
+    return callback.visit(this, component);
+  }
+
+  // The FacesContext for this request
+  private final FacesContext _facesContext;
+
+  // Our visit hints
+  private final Set<VisitHint> _hints;
+}
\ No newline at end of file

Modified: myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java
URL: http://svn.apache.org/viewvc/myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java?rev=979940&r1=979939&r2=979940&view=diff
==============================================================================
--- myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java (original)
+++ myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java Wed Jul 28 02:50:58 2010
@@ -17,12 +17,13 @@
 
 package org.apache.myfaces.test.mock.visit;
 
+import java.util.Collection;
+import java.util.Set;
+
 import javax.faces.component.visit.VisitContext;
 import javax.faces.component.visit.VisitContextFactory;
 import javax.faces.component.visit.VisitHint;
 import javax.faces.context.FacesContext;
-import java.util.Collection;
-import java.util.Set;
 
 /**
  * <p>Mock implementation of <code>VisitContextFactory</code>.</p>
@@ -37,6 +38,10 @@ public class MockVisitContextFactory ext
     @Override
     public VisitContext getVisitContext(FacesContext context, Collection<String> ids, Set<VisitHint> hints)
     {
-        return new MockVisitContext(context, hints);
+        if (ids == null || ids.isEmpty()) {
+            return new FullVisitContext(context, hints);
+        } else {
+            return new PartialVisitContext(context, ids, hints);
+        }
     }
 }

Added: myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java
URL: http://svn.apache.org/viewvc/myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java?rev=979940&view=auto
==============================================================================
--- myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java (added)
+++ myfaces/test/trunk/test20/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java Wed Jul 28 02:50:58 2010
@@ -0,0 +1,461 @@
+/*
+ * 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.test.mock.visit;
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UINamingContainer;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>A VisitContext implementation that is
+ * used when performing a partial component tree visit.</p>
+ * 
+ * @author Werner Punz, Blake Sullivan (latest modification by $Author: lu4242 $)
+ * @version $Rev: 949094 $ $Date: 2010-05-27 22:59:10 -0500 (Jue, 27 May 2010) $
+ */
+public class PartialVisitContext extends VisitContext
+{
+
+  /**
+   * Creates a PartialVisitorContext instance.
+   * @param facesContext the FacesContext for the current request
+   * @param clientIds the client ids of the components to visit
+   * @throws NullPointerException  if {@code facesContext}
+   *                               is {@code null}
+   */    
+  public PartialVisitContext(
+    FacesContext facesContext,
+    Collection<String> clientIds)
+  {
+    this(facesContext, clientIds, null);
+  }
+
+  /**
+   * Creates a PartialVisitorContext instance with the specified hints.
+   * @param facesContext the FacesContext for the current request
+   * @param clientIds the client ids of the components to visit
+   * @param hints a the VisitHints for this visit
+   * @throws NullPointerException  if {@code facesContext}
+   *                               is {@code null}
+   * @throws IllegalArgumentException if the phaseId is specified and
+   * hints does not contain VisitHint.EXECUTE_LIFECYCLE
+   */    
+  public PartialVisitContext(FacesContext facesContext,
+                             Collection<String> clientIds,
+                             Set<VisitHint> hints)
+  {
+    if (facesContext == null)
+      throw new NullPointerException();
+
+    _facesContext = facesContext;
+
+    // Copy the client ids into a HashSet to allow for quick lookups.
+    Set<String> clientIdSet = (clientIds == null)
+                                ? new HashSet<String>()
+                                : new HashSet<String>(clientIds);
+
+    // Initialize our various collections
+    // We maintain 4 collections:
+    //
+    // 1. clientIds: contains all of the client ids to visit
+    // 2. ids: contains just ids (not client ids) to visit.
+    //    We use this to optimize our check to see whether a
+    //    particular component is in the visit set (ie. to
+    //    avoid having to compute the client id).
+    // 3. subtreeClientIds: contains client ids to visit broken
+    //    out by naming container subtree.  (Needed by
+    //    getSubtreeIdsToVisit()).
+    // 4. unvisitedClientIds: contains the client ids to visit that
+    //    have not yet been visited.
+    //
+    // We populate these now.
+    //
+    // Note that we use default HashSet/Map initial capacities, though
+    // perhaps we could pick more intelligent defaults.
+
+    // Initialize unvisitedClientIds collection
+    _unvisitedClientIds = new HashSet<String>();
+
+    // Initialize ids collection
+    _ids = new HashSet<String>();
+
+    // Intialize subtreeClientIds collection
+    _subtreeClientIds = new HashMap<String, Collection<String>>();
+
+    // Initialize the clientIds collection.  Note that we proxy 
+    // this collection so that we can trap adds/removes and sync 
+    // up all of the other collections.
+    _clientIds = new CollectionProxy<String>(new HashSet<String>());
+
+    // Finally, populate the clientIds collection.  This has the
+    // side effect of populating all of the other collections.       
+    _clientIds.addAll(clientIdSet);
+
+    // Copy and store hints - ensure unmodifiable and non-empty
+    EnumSet<VisitHint> hintsEnumSet = ((hints == null) || (hints.isEmpty()))
+                                        ? EnumSet.noneOf(VisitHint.class)
+                                        : EnumSet.copyOf(hints);
+
+    _hints = Collections.unmodifiableSet(hintsEnumSet);
+  }
+
+  /**
+   * @see VisitContext#getFacesContext VisitContext.getFacesContext()
+   */
+  @Override
+  public FacesContext getFacesContext()
+  {
+    return _facesContext;
+  }
+
+  /**
+   * @see VisitContext#getHints VisitContext.getHints
+   */
+  @Override
+  public Set<VisitHint> getHints()
+  {
+    return _hints;
+  }
+
+  /**
+   * @see VisitContext#getIdsToVisit VisitContext.getIdsToVisit()
+   */
+  @Override
+  public Collection<String> getIdsToVisit()
+  {
+    // We just return our clientIds collection.  This is
+    // the modifiable (but proxied) collection of all of
+    // the client ids to visit.
+    return _clientIds;
+  }
+
+  /**
+   * @see VisitContext#getSubtreeIdsToVisit VisitContext.getSubtreeIdsToVisit()
+   */
+  @Override
+  public Collection<String> getSubtreeIdsToVisit(UIComponent component)
+  {
+    // Make sure component is a NamingContainer
+    if (!(component instanceof NamingContainer))
+    {
+      throw new IllegalArgumentException("Component is not a NamingContainer: " + component);
+    }
+
+    String clientId = component.getClientId(getFacesContext());
+    Collection<String> ids = _subtreeClientIds.get(clientId);
+
+    if (ids == null)
+      return Collections.emptyList();
+    else
+      return Collections.unmodifiableCollection(ids);     
+  }
+
+  /**
+   * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback()
+   */
+  @Override
+  public VisitResult invokeVisitCallback(
+    UIComponent component, 
+    VisitCallback callback)
+  {
+    // First sure that we should visit this component - ie.
+    // that this component is represented in our id set.
+    String clientId = _getVisitId(component);
+
+    if (clientId == null)
+    {
+      // Not visiting this component, but allow visit to
+      // continue into this subtree in case we've got
+      // visit targets there.
+      return VisitResult.ACCEPT;
+    }
+
+    // If we made it this far, the component matches one of
+    // client ids, so perform the visit.
+    VisitResult result = callback.visit(this, component);
+
+    // Remove the component from our "unvisited" collection
+    _unvisitedClientIds.remove(clientId);
+
+    // If the unvisited collection is now empty, we are done.
+    // Return VisitResult.COMPLETE to terminate the visit.
+    if (_unvisitedClientIds.isEmpty())
+      return VisitResult.COMPLETE;
+    else
+    {
+      // Otherwise, just return the callback's result 
+      return result;
+    }
+  }
+
+
+  // Called by CollectionProxy to notify PartialVisitContext that
+  // an new id has been added.
+  private void _idAdded(String clientId)
+  {
+    // An id to visit has been added, update our other
+    // collections to reflect this.
+
+    // Update the ids collection
+    _ids.add(_getIdFromClientId(clientId));
+
+    // Update the unvisited ids collection
+    _unvisitedClientIds.add(clientId);
+
+    // Update the subtree ids collection
+    _addSubtreeClientId(clientId);
+  }
+
+  // Called by CollectionProxy to notify PartialVisitContext that
+  // an id has been removed
+  private void _idRemoved(String clientId)
+  {
+    // An id to visit has been removed, update our other
+    // collections to reflect this.  Note that we don't
+    // update the ids collection, since we ids (non-client ids)
+    // may not be unique.
+
+    // Update the unvisited ids collection
+    _unvisitedClientIds.remove(clientId);
+
+    // Update the subtree ids collection
+    _removeSubtreeClientId(clientId);
+  }
+
+  // Tests whether the specified component should be visited.
+  // If so, returns its client id.  If not, returns null.
+  private String _getVisitId(UIComponent component)
+  {
+    // We first check to see whether the component's id
+    // is in our id collection.  We do this before checking
+    // for the full client id because getting the full client id
+    // is more expensive than just getting the local id.
+    String id = component.getId();
+
+    if ((id != null) && !_ids.contains(id))
+      return null;
+
+    // The id was a match - now check the client id.
+    // note that client id should never be null (should be
+    // generated even if id is null, so asserting this.)
+    String clientId = component.getClientId(getFacesContext());
+    assert(clientId != null);
+
+    return _clientIds.contains(clientId) ? clientId : null;
+  }
+
+
+
+  // Converts an client id into a plain old id by ripping
+  // out the trailing id segmetn.
+  private String _getIdFromClientId(String clientId)
+  {
+    final char separator = UINamingContainer.getSeparatorChar(_facesContext);
+    int lastIndex = clientId.lastIndexOf(separator);
+
+    String id = null;
+
+    if (lastIndex < 0)
+    {
+      id = clientId;
+    }
+    else if (lastIndex < (clientId.length() - 1))
+    {
+      id = clientId.substring(lastIndex + 1);              
+    }
+    else
+    {
+      // TODO log warning for trailing colon case
+    }
+ 
+    return id;
+  }
+
+
+  // Given a single client id, populate the subtree map with all possible
+  // subtree client ids
+  private void _addSubtreeClientId(String clientId)
+  {
+    // Loop over the client id and find the substring corresponding to
+    // each ancestor NamingContainer client id.  For each ancestor
+    // NamingContainer, add an entry into the map for the full client
+    // id.
+    final char separator = UINamingContainer.getSeparatorChar(_facesContext);
+    
+    int length = clientId.length();
+
+    for (int i = 0; i < length; i++)
+    {
+      if (clientId.charAt(i) == separator)
+      {
+        // We found an ancestor NamingContainer client id - add 
+        // an entry to the map.
+        String namingContainerClientId = clientId.substring(0, i);
+
+        // Check to see whether we've already ids under this
+        // NamingContainer client id.  If not, create the 
+        // Collection for this NamingContainer client id and
+        // stash it away in our map
+        Collection<String> c = _subtreeClientIds.get(namingContainerClientId);
+
+        if (c == null)
+        {
+          // TODO: smarter initial size?
+          c = new ArrayList<String>();
+          _subtreeClientIds.put(namingContainerClientId, c);
+        }
+
+        // Stash away the client id
+        c.add(clientId);
+      }
+    }
+  }
+
+  // Given a single client id, remove any entries corresponding
+  // entries from our subtree collections
+  private void _removeSubtreeClientId(String clientId)
+  {
+    // Loop through each entry in the map and check to see whether
+    // the client id to remove should be contained in the corresponding
+    // collection - ie. whether the key (the NamingContainer client id)
+    // is present at the start of the client id to remove.
+    for (String key : _subtreeClientIds.keySet())
+    {
+      if (clientId.startsWith(key))
+      {
+        // If the clientId starts with the key, we should
+        // have an entry for this clientId in the corresponding
+        // collection.  Remove it.
+        Collection<String> ids = _subtreeClientIds.get(key);
+        ids.remove(clientId);
+      }
+    }
+  }
+
+  // Little proxy collection implementation.  We proxy the id
+  // collection so that we can detect modifications and update
+  // our internal state when ids to visit are added or removed.
+  private class CollectionProxy<E extends String> extends AbstractCollection<E>
+  {
+    private CollectionProxy(Collection<E> wrapped)
+    {
+      _wrapped = wrapped;
+    }
+
+    @Override
+    public int size()
+    {
+      return _wrapped.size();
+    }
+
+    @Override
+    public Iterator<E> iterator()
+    {
+      return new IteratorProxy<E>(_wrapped.iterator());
+    }
+
+    @Override
+    public boolean add(E o)
+    {
+      boolean added = _wrapped.add(o);
+
+      if (added)
+      {
+        _idAdded(o);
+      }
+
+      return added;
+    }
+
+    private final Collection<E> _wrapped;
+  }
+
+  // Little proxy iterator implementation used by CollectionProxy
+  // so that we can catch removes.
+  private class IteratorProxy<E extends String> implements Iterator<E>
+  {
+    private IteratorProxy(Iterator<E> wrapped)
+    {
+      _wrapped = wrapped;
+    }
+
+    public boolean hasNext()
+    {
+      return _wrapped.hasNext();
+    }
+
+    public E next()
+    {
+      _current = _wrapped.next();
+      
+      return _current;
+    }
+
+    public void remove()
+    {
+      if (_current != null)
+      {
+        _idRemoved(_current);
+      }
+
+      _wrapped.remove();
+    }
+
+    private final Iterator<E> _wrapped;
+
+    private E _current = null;
+  }
+
+  // The client ids to visit
+  private final Collection<String> _clientIds;
+
+  // The ids to visit
+  private final Collection<String> _ids;
+
+  // The client ids that have yet to be visited
+  private final Collection<String> _unvisitedClientIds;
+
+  // This map contains the information needed by getIdsToVisit().
+  // The keys in this map are NamingContainer client ids.  The values
+  // are collections containing all of the client ids to visit within
+  // corresponding naming container.
+  private final Map<String,Collection<String>> _subtreeClientIds;
+
+  // The FacesContext for this request
+  private final FacesContext _facesContext;
+
+  // Our visit hints
+  private final Set<VisitHint> _hints;
+}
\ No newline at end of file