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