You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/02/22 04:26:16 UTC

svn commit: r510369 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/internal/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/test/pagelevel/ test/app1/WEB-IN...

Author: hlship
Date: Wed Feb 21 19:26:14 2007
New Revision: 510369

URL: http://svn.apache.org/viewvc?view=rev&rev=510369
Log:
Support page activation contexts within action request URLs.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Kicker.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Target.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Kicker.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Target.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandler.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandlerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInvocation.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentInvocationTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java Wed Feb 21 19:26:14 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 
 import java.util.List;
 
+import org.apache.commons.codec.net.URLCodec;
 import org.apache.tapestry.services.Dispatcher;
 
 /**
@@ -45,7 +46,8 @@
     String getParameterValue(String name);
 
     /**
-     * Adds a parameter value.
+     * Adds a parameter value. The value will be added, as is, to the URL. In many cases, the value
+     * should be URL encoded via {@link URLCodec}.
      * 
      * @param parameterName
      *            the name of the parameter to store

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalConstants.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalConstants.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalConstants.java Wed Feb 21 19:26:14 2007
@@ -33,6 +33,10 @@
     /** All purpose CSS class name for anything related to Tapestry errors. */
     public static final String TAPESTRY_ERROR_CLASS = "t-error";
 
+    /** The name of the query parameter that stores the page context inside an action request. */
+
+    public static final String PAGE_CONTEXT_NAME = "t:ac";
+
     private InternalConstants()
     {
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java Wed Feb 21 19:26:14 2007
@@ -23,6 +23,9 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.net.URLCodec;
 import org.apache.tapestry.OptionModel;
 import org.apache.tapestry.PropertyConduit;
 import org.apache.tapestry.SelectModel;
@@ -38,6 +41,8 @@
 /** Shared utility methods used by various implementation classes. */
 public class TapestryUtils
 {
+    private static final URLCodec CODEC = new URLCodec();
+
     private TapestryUtils()
     {
         // Prevent instantiation.
@@ -208,7 +213,7 @@
      *            map of elements
      * @return list of option models
      */
-    public static <K,V> List<OptionModel> toOptionModels(Map<K,V> input)
+    public static <K, V> List<OptionModel> toOptionModels(Map<K, V> input)
     {
         Defense.notNull(input, "input");
 
@@ -227,7 +232,7 @@
      * @param input
      * @return
      */
-    public static <K,V> SelectModel toSelectModel(Map<K,V> input)
+    public static <K, V> SelectModel toSelectModel(Map<K, V> input)
     {
         List<OptionModel> options = toOptionModels(input);
 
@@ -406,5 +411,29 @@
         String prefix = IOCUtilities.toSimpleId(value.getClass().getName());
 
         return getLabelForEnum(messages, prefix, value);
+    }
+
+    public static String urlEncode(String input)
+    {
+        try
+        {
+            return CODEC.encode(input);
+        }
+        catch (EncoderException ex)
+        {
+            throw new RuntimeException(ex);
+        }
+    }
+    
+    public static String urlDecode(String input)
+    {
+        try
+        {
+            return CODEC.decode(input);
+        }
+        catch (DecoderException ex)
+        {
+            throw new RuntimeException(ex);
+        }
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandler.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandler.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandler.java Wed Feb 21 19:26:14 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -31,5 +31,5 @@
      * send the final response to the client.
      */
     ActionResponseGenerator handle(String logicalPageName, String nestedComponentId,
-            String eventType, String[] context);
+            String eventType, String[] context, String[] activationContext);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandlerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandlerImpl.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandlerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkHandlerImpl.java Wed Feb 21 19:26:14 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry.ComponentEventHandler;
 import org.apache.tapestry.Link;
+import org.apache.tapestry.TapestryConstants;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.internal.util.Holder;
@@ -41,12 +42,13 @@
     }
 
     public ActionResponseGenerator handle(String logicalPageName, String nestedComponentId,
-            String eventType, String[] context)
+            String eventType, String[] context, String[] activationContext)
     {
         ActionLinkTarget actionLinkTarget = new ActionLinkTarget(eventType, logicalPageName,
                 nestedComponentId);
 
-        ComponentInvocation invocation = new ComponentInvocation(actionLinkTarget, context);
+        ComponentInvocation invocation = new ComponentInvocation(actionLinkTarget, context,
+                activationContext);
 
         return handle(invocation);
     }
@@ -84,6 +86,17 @@
             }
         };
 
+        // If activating the page returns a "navigational result", then don't trigger the action
+        // on the component.
+
+        page.getRootElement().triggerEvent(
+                TapestryConstants.ACTIVATE_EVENT,
+                invocation.getActivationContext(),
+                handler);
+
+        if (holder.hasValue())
+            return holder.get();
+
         element.triggerEvent(actionLinkTarget.getAction(), invocation.getContext(), handler);
 
         ActionResponseGenerator result = holder.get();
@@ -92,7 +105,7 @@
         {
             Link link = _linkFactory.createPageLink(page);
 
-            return new LinkActionResponseGenerator(link);
+            result = new LinkActionResponseGenerator(link);
         }
 
         return result;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java Wed Feb 21 19:26:14 2007
@@ -16,6 +16,8 @@
 
 import java.io.IOException;
 
+import org.apache.tapestry.internal.InternalConstants;
+import org.apache.tapestry.internal.TapestryUtils;
 import org.apache.tapestry.services.ActionResponseGenerator;
 import org.apache.tapestry.services.Dispatcher;
 import org.apache.tapestry.services.Request;
@@ -29,6 +31,8 @@
 {
     private final ActionLinkHandler _actionLinkHandler;
 
+    private final String[] _emptyString = new String[0];
+
     public ComponentActionDispatcher(ActionLinkHandler actionLinkHandler)
     {
         _actionLinkHandler = actionLinkHandler;
@@ -57,18 +61,36 @@
 
         String eventType = path.substring(lastDotx + 1, slashx);
 
-        String[] context = slashx < path.length() ? path.substring(slashx + 1).split("/")
-                : new String[0];
+        String[] context = slashx < path.length() ? decodeContext(path.substring(slashx + 1))
+                : _emptyString;
+
+        String activationContextValue = request.getParameter(InternalConstants.PAGE_CONTEXT_NAME);
+
+        String[] activationContext = activationContextValue == null ? _emptyString
+                : decodeContext(activationContextValue);
 
         ActionResponseGenerator responseGenerator = _actionLinkHandler.handle(
                 logicalPageName,
                 nestedComponentId,
                 eventType,
-                context);
+                context,
+                activationContext);
 
         responseGenerator.sendClientResponse(response);
 
         return true;
+    }
+
+    private String[] decodeContext(String input)
+    {
+        String[] result = input.split("/");
+
+        for (int i = 0; i < result.length; i++)
+        {
+            result[i] = TapestryUtils.urlDecode(result[i]);
+        }
+
+        return result;
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInvocation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInvocation.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInvocation.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInvocation.java Wed Feb 21 19:26:14 2007
@@ -19,8 +19,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.codec.EncoderException;
-import org.apache.commons.codec.net.URLCodec;
+import org.apache.tapestry.internal.TapestryUtils;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.test.pagelevel.PageTester;
 
@@ -33,14 +32,27 @@
 {
     private final String[] _context;
 
-    private Map<String, String> _parameters;
+    private final InvocationTarget _target;
+
+    private final String[] _activationContext;
 
-    private InvocationTarget _target;
+    private Map<String, String> _parameters;
 
-    public ComponentInvocation(InvocationTarget target, String[] context)
+    /**
+     * @param target
+     *            identifies the target of the event: a component with a page
+     * @param context
+     *            context information supplied by the component to be provided back when the event
+     *            on the component is triggered
+     * @param activationContext
+     *            page activation context for the page containing the component, supplied via a
+     *            passivate event to the page's root component
+     */
+    public ComponentInvocation(InvocationTarget target, String[] context, String[] activationContext)
     {
         _target = target;
         _context = context;
+        _activationContext = activationContext;
     }
 
     /**
@@ -58,30 +70,22 @@
 
         builder.append(path);
 
-        try
-        {
-            URLCodec codec = new URLCodec();
-
-            String sep = "?";
+        String sep = "?";
 
-            for (String name : getParameterNames())
-            {
-                String value = _parameters.get(name);
+        for (String name : getParameterNames())
+        {
+            String value = _parameters.get(name);
 
-                builder.append(sep);
+            builder.append(sep);
 
-                // TODO: encode the parameter name?
+            // We assume that the name is URL safe and that the value will already have been URL
+            // encoded if it is not known to be URL safe.
 
-                builder.append(name);
-                builder.append("=");
-                builder.append(codec.encode(value));
+            builder.append(name);
+            builder.append("=");
+            builder.append(value);
 
-                sep = "&";
-            }
-        }
-        catch (EncoderException ex)
-        {
-            throw new RuntimeException(ex);
+            sep = "&";
         }
 
         return builder.toString();
@@ -96,20 +100,24 @@
         StringBuilder builder = new StringBuilder();
         builder.append(_target.getPath());
 
-        for (Object id : _context)
+        for (String id : _context)
         {
             builder.append("/");
 
-            // TODO: Need to encode this for URLs? What if the string contains slashes, etc.?
-
-            builder.append(id);
+            builder.append(TapestryUtils.urlEncode(id));
         }
+
         return builder.toString();
     }
 
     public String[] getContext()
     {
         return _context;
+    }
+
+    public String[] getActivationContext()
+    {
+        return _activationContext;
     }
 
     public void addParameter(String parameterName, String value)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Wed Feb 21 19:26:14 2007
@@ -17,17 +17,20 @@
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newThreadSafeList;
+import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
+import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
 
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.codec.net.URLCodec;
 import org.apache.tapestry.ComponentEventHandler;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.ioc.internal.util.Defense;
 import org.apache.tapestry.ioc.services.TypeCoercer;
 import org.apache.tapestry.ioc.util.StrategyRegistry;
 import org.apache.tapestry.runtime.Component;
@@ -51,6 +54,9 @@
 
     private final StrategyRegistry<PassivateContextHandler> _registry;
 
+    // URLCodec is thread-safe and sharable.
+    private final URLCodec _urlCodec = new URLCodec();
+
     private interface PassivateContextHandler<T>
     {
         void handle(T result, List context);
@@ -110,9 +116,10 @@
     public Link createActionLink(ComponentPageElement component, String action, boolean forForm,
             Object... context)
     {
-        Defense.notBlank(action, "action");
+        notBlank(action, "action");
 
-        String pageName = component.getContainingPage().getName();
+        Page containingPage = component.getContainingPage();
+        String pageName = containingPage.getName();
 
         String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
 
@@ -121,27 +128,81 @@
 
         String[] contextStrings = toContextStrings(context);
 
-        ComponentInvocation invocation = new ComponentInvocation(target, contextStrings);
+        String[] activationContext = collectActivationContextForPage(containingPage);
+
+        ComponentInvocation invocation = new ComponentInvocation(target, contextStrings,
+                activationContext);
 
         Link link = new LinkImpl(_encoder, _contextPathSource.getContextPath(), invocation, forForm);
 
+        // Now see if the page has an activation context.
+
+        addActivationContextToLink(link, activationContext);
+
+        // TODO: query parameter for case where active page != component page.
+
         _componentInvocationMap.store(link, invocation);
 
         for (LinkFactoryListener listener : _listeners)
             listener.createdActionLink(link);
 
-        // TODO: query parameter for case where active page != component page.
-
         return link;
     }
 
+    private void addActivationContextToLink(Link link, String[] activationContext)
+    {
+        if (activationContext.length == 0)
+            return;
+
+        StringBuilder builder = new StringBuilder();
+
+        try
+        {
+            for (int i = 0; i < activationContext.length; i++)
+            {
+                if (i > 0)
+                    builder.append("/");
+
+                builder.append(_urlCodec.encode(activationContext[i]));
+            }
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(ex);
+        }
+
+        link.addParameter(InternalConstants.PAGE_CONTEXT_NAME, builder.toString());
+
+    }
+
     public Link createPageLink(final Page page)
     {
-        Defense.notNull(page, "page");
+        notNull(page, "page");
 
         String pageName = page.getName();
         String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
 
+        String[] context = collectActivationContextForPage(page);
+
+        PageLinkTarget target = new PageLinkTarget(logicalPageName);
+        ComponentInvocation invocation = new ComponentInvocation(target, context, null);
+
+        Link link = new LinkImpl(_encoder, _contextPathSource.getContextPath(), invocation, false);
+
+        _componentInvocationMap.store(link, invocation);
+
+        for (LinkFactoryListener listener : _listeners)
+            listener.createdPageLink(link);
+
+        return link;
+    }
+
+    /**
+     * Returns a list of objects acquired by invoking triggering the passivate event on the page's
+     * root element. May return an empty list.
+     */
+    private String[] collectActivationContextForPage(final Page page)
+    {
         final List context = newList();
 
         ComponentEventHandler handler = new ComponentEventHandler()
@@ -161,20 +222,7 @@
 
         rootElement.triggerEvent(TapestryConstants.PASSIVATE_EVENT, null, handler);
 
-        PageLinkTarget target = new PageLinkTarget(logicalPageName);
-        ComponentInvocation invocation = new ComponentInvocation(target, toContextStrings(context
-                .toArray()));
-
-        Link link = new LinkImpl(_encoder, _contextPathSource.getContextPath(), invocation, false);
-
-        _componentInvocationMap.store(link, invocation);
-
-        for (LinkFactoryListener listener : _listeners)
-            listener.createdPageLink(link);
-
-        // TODO: query parameter for case where active page != component page.
-
-        return link;
+        return toContextStrings(context.toArray());
     }
 
     private String[] toContextStrings(Object[] context)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java Wed Feb 21 19:26:14 2007
@@ -39,7 +39,7 @@
     public LinkImpl(URLEncoder encoder, String contextPath, String targetPath, boolean forForm)
     {
         this(encoder, contextPath, new ComponentInvocation(new OpaqueConstantTarget(targetPath),
-                new String[0]), forForm);
+                new String[0], null), forForm);
     }
 
     public LinkImpl(URLEncoder encoder, String contextPath, ComponentInvocation invocation,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java Wed Feb 21 19:26:14 2007
@@ -44,7 +44,7 @@
             PageRenderer renderer)
     {
         PageLinkTarget target = new PageLinkTarget(logicalPageName);
-        ComponentInvocation invocation = new ComponentInvocation(target, context);
+        ComponentInvocation invocation = new ComponentInvocation(target,  context, context);
 
         return handle(invocation, renderer);
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java Wed Feb 21 19:26:14 2007
@@ -16,6 +16,7 @@
 
 import java.io.IOException;
 
+import org.apache.tapestry.internal.TapestryUtils;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.services.ActionResponseGenerator;
 import org.apache.tapestry.services.ComponentClassResolver;
@@ -65,10 +66,8 @@
 
             if (_componentClassResolver.isPageName(pageName))
             {
-                // TODO: UUDecode the strings in the context?
-
-                String[] context = atEnd ? new String[0] : path.substring(nextslashx + 1)
-                        .split("/");
+                String[] context = atEnd ? new String[0] : convertActivationContext(path
+                        .substring(nextslashx + 1));
 
                 PageRenderer renderer = new PageRenderer()
                 {
@@ -103,5 +102,25 @@
 
             searchStart = nextslashx + 1;
         }
+    }
+
+    /**
+     * Converts the "extra path", the portion after the page name (and after the slash seperating
+     * the page name from the activation context) into an array of strings. LinkFactory and friends
+     * URL encode each value, so we URL decode the value (we assume that page names are "URL safe").
+     * 
+     * @param extraPath
+     * @return
+     */
+    private String[] convertActivationContext(String extraPath)
+    {
+        String[] context = extraPath.split("/");
+
+        for (int i = 0; i < context.length; i++)
+        {
+            context[i] = TapestryUtils.urlDecode(context[i]);
+        }
+
+        return context;
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java Wed Feb 21 19:26:14 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -181,7 +181,7 @@
      */
     public Document renderPage(String pageName)
     {
-        return invoke(new ComponentInvocation(new PageLinkTarget(pageName), new String[0]));
+        return invoke(new ComponentInvocation(new PageLinkTarget(pageName), new String[0], null));
     }
 
     /**

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Kicker.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Kicker.html?view=auto&rev=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Kicker.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Kicker.html Wed Feb 21 19:26:14 2007
@@ -0,0 +1,8 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+<h1>Kicker</h1>    
+
+[<a t:type="actionlink">kick target</a>]
+
+
+</html>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Start.html Wed Feb 21 19:26:14 2007
@@ -110,6 +110,9 @@
                         <a t:type="PageLink" page="protected">Protected Page</a> -- Demonstrate result of non-void return from
                         a page's activate method.
                     </li>
+                    <li>
+                        <a t:type="PageLink" page="kicker">Kicker</a> -- demos complex page and component context in links
+                    </li>
                 </ul>
             </td>
         </tr>

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Target.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Target.html?view=auto&rev=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Target.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/Target.html Wed Feb 21 19:26:14 2007
@@ -0,0 +1,29 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    
+    <h1>Target</h1>    
+    
+  
+  <h2>Activation Context</h2>
+  
+    <ul>
+        <li t:type="loop" source="activationContext" value="object">${object}</li>
+    </ul>
+  
+  <h2>Component Context</h2>
+  
+    <t:comp type="if" test="componentContext">
+        
+        <ul>
+            <li t:type="loop" source="componentContext" value="object">${object}</li>
+        </ul>
+
+        <t:parameter name="else">
+            No component context.
+        </t:parameter>
+    </t:comp>
+  
+  <h2>Setup Component Context</h2>
+  
+  [<a t:type="actionlink" context="contextToEncode">go</a>]
+    
+</html>
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Wed Feb 21 19:26:14 2007
@@ -886,4 +886,21 @@
         assertTextPresent("Access to Protected page is denied");
     }
 
+    @Test
+    public void mixed_page_activation_context_and_component_context()
+    {
+        _selenium.open(BASE_URL);
+
+        clickAndWait("link=Kicker");
+
+        clickAndWait("actionlink");
+
+        assertTextSeries("//li[%d]", 1, "betty", "wilma");
+        assertTextPresent("No component context.");
+
+        clickAndWait("link=go");
+
+        assertTextSeries("//li[%d]", 1, "betty", "wilma");
+        assertTextSeries("//ul[2]/li[%d]", 1, "fred", "barney", "clark kent");
+    }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Kicker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Kicker.java?view=auto&rev=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Kicker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Kicker.java Wed Feb 21 19:26:14 2007
@@ -0,0 +1,31 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed 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.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.InjectPage;
+
+public class Kicker
+{
+    @InjectPage
+    private Target _target;
+
+    Object onAction()
+    {
+        _target.setActivationContext(new String[]
+        { "betty", "wilma" });
+
+        return _target;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Target.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Target.java?view=auto&rev=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Target.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Target.java Wed Feb 21 19:26:14 2007
@@ -0,0 +1,66 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed 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.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.OnEvent;
+import org.apache.tapestry.annotations.Persist;
+
+public class Target
+{
+    private Object[] _activationContext;
+
+    @Persist("flash")
+    private Object[] _componentContext;
+
+    private Object _object;
+
+    @OnEvent("passivate")
+    public Object[] getActivationContext()
+    {
+        return _activationContext;
+    }
+
+    public Object[] getComponentContext()
+    {
+        return _componentContext;
+    }
+
+    @OnEvent("activate")
+    public void setActivationContext(Object[] activationContext)
+    {
+        _activationContext = activationContext;
+    }
+
+    void onAction(Object[] componentContext)
+    {
+        _componentContext = componentContext;
+    }
+
+    public Object[] getContextToEncode()
+    {
+        return new Object[]
+        { "fred", "barney", "clark kent" };
+    }
+
+    public Object getObject()
+    {
+        return _object;
+    }
+
+    public void setObject(Object object)
+    {
+        _object = object;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java Wed Feb 21 19:26:14 2007
@@ -14,16 +14,17 @@
 
 package org.apache.tapestry.internal.services;
 
-import java.io.IOException;
-
+import static org.easymock.EasyMock.aryEq;
 import static org.easymock.EasyMock.eq;
 
+import java.io.IOException;
+
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.services.ActionResponseGenerator;
 import org.apache.tapestry.services.Dispatcher;
 import org.apache.tapestry.services.Request;
 import org.apache.tapestry.services.Response;
-import org.easymock.EasyMock;
 import org.testng.annotations.Test;
 
 public class ComponentActionDispatcherTest extends InternalBaseTestCase
@@ -98,9 +99,15 @@
 
         train_getPath(request, requestPath);
 
+        train_getParameter(request, InternalConstants.PAGE_CONTEXT_NAME, null);
+
         expect(
-                handler.handle(eq(logicalPageName), eq(nestedComponentId), eq(eventType), EasyMock
-                        .aryEq(context))).andReturn(generator);
+                handler.handle(
+                        eq(logicalPageName),
+                        eq(nestedComponentId),
+                        eq(eventType),
+                        aryEq(context),
+                        aryEq(new String[0]))).andReturn(generator);
 
         generator.sendClientResponse(response);
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentInvocationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentInvocationTest.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentInvocationTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentInvocationTest.java Wed Feb 21 19:26:14 2007
@@ -23,7 +23,7 @@
     public void no_context()
     {
         ComponentInvocation invocation = new ComponentInvocation(new OpaqueConstantTarget("abc"),
-                new String[0]);
+                new String[0], null);
         assertEquals(invocation.buildURI(false), "abc");
         assertEquals(invocation.buildURI(true), "abc");
     }
@@ -33,7 +33,7 @@
     {
         ComponentInvocation invocation = new ComponentInvocation(new OpaqueConstantTarget("abc"),
                 new String[]
-                { "x", "123" });
+                { "x", "123" }, null);
         assertEquals(invocation.buildURI(false), "abc/x/123");
         assertEquals(invocation.buildURI(true), "abc/x/123");
     }
@@ -43,7 +43,7 @@
     {
         ComponentInvocation invocation = new ComponentInvocation(new OpaqueConstantTarget("abc"),
                 new String[]
-                { "x", "123" });
+                { "x", "123" }, null);
         invocation.addParameter("p1", "foo");
         invocation.addParameter("p2", "bar");
         assertEquals(invocation.buildURI(false), "abc/x/123?p1=foo&p2=bar");

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java Wed Feb 21 19:26:14 2007
@@ -14,11 +14,14 @@
 
 package org.apache.tapestry.internal.services;
 
+import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.isNull;
 
 import org.apache.tapestry.ComponentEventHandler;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
@@ -105,59 +108,21 @@
         LinkFactoryListener listener = newLinkFactoryListener();
         ComponentInvocationMap map = newComponentInvocationMap();
 
-        final Holder<Link> holder = new Holder<Link>();
-
         train_getName(page, PAGE_CLASS_NAME);
         train_resolvePageClassNameToPageName(resolver, PAGE_CLASS_NAME, PAGE_LOGICAL_NAME);
         train_getContextPath(request, "/barney");
 
         train_getRootElement(page, rootElement);
 
-        // Answer for triggerEvent() which returns a boolean.
-
-        IAnswer<Boolean> triggerEventAnswer = new IAnswer<Boolean>()
-        {
-            public Boolean answer() throws Throwable
-            {
-                ComponentEventHandler handler = (ComponentEventHandler) EasyMock
-                        .getCurrentArguments()[2];
-
-                handler.handleResult(new Object[]
-                { "foo", "bar" }, null, null);
-
-                return true;
-            }
-        };
-
-        IAnswer<Void> createdPageLinkAnswer = new IAnswer<Void>()
-        {
-            public Void answer() throws Throwable
-            {
-                Link link = (Link) EasyMock.getCurrentArguments()[0];
-
-                holder.put(link);
-
-                return null;
-            }
-        };
+        final Holder<Link> holder = new Holder<Link>();
 
-        // Intercept the call to handle component event, and let the IAnswer
-        // do the work.
-
-        expect(
-                rootElement.triggerEvent(
-                        EasyMock.eq(TapestryConstants.PASSIVATE_EVENT),
-                        (Object[]) EasyMock.isNull(),
-                        EasyMock.isA(ComponentEventHandler.class))).andAnswer(triggerEventAnswer);
+        train_triggerPassivateEventForPageLink(rootElement, listener, holder);
 
         train_encodeRedirectURL(
                 response,
                 "/barney/" + PAGE_LOGICAL_NAME.toLowerCase() + "/foo/bar",
                 ENCODED);
 
-        listener.createdPageLink(EasyMock.isA(Link.class));
-        getMocksControl().andAnswer(createdPageLinkAnswer);
-
         // This needs to be refactored a bit to be more testable.
 
         map.store(isA(Link.class), isA(ComponentInvocation.class));
@@ -179,7 +144,6 @@
         verify();
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void page_link_by_name()
     {
@@ -192,8 +156,6 @@
         ComponentInvocationMap map = newComponentInvocationMap();
         RequestPageCache cache = newRequestPageCache();
 
-        final Holder<Link> holder = new Holder<Link>();
-
         train_get(cache, PAGE_LOGICAL_NAME, page);
 
         train_getName(page, PAGE_CLASS_NAME);
@@ -203,7 +165,40 @@
         train_getRootElement(page, rootElement);
 
         // Answer for triggerEvent() which returns a boolean.
+        final Holder<Link> holder = new Holder<Link>();
+
+        train_triggerPassivateEventForPageLink(rootElement, listener, holder);
+
+        train_encodeRedirectURL(
+                response,
+                "/barney/" + PAGE_LOGICAL_NAME.toLowerCase() + "/foo/bar",
+                ENCODED);
+
+        // This needs to be refactored a bit to be more testable.
+
+        map.store(isA(Link.class), isA(ComponentInvocation.class));
+
+        replay();
+
+        LinkFactory factory = new LinkFactoryImpl(request, response, resolver, map, cache,
+                _typeCoercer);
+        factory.addListener(listener);
+
+        Link link = factory.createPageLink(PAGE_LOGICAL_NAME);
+
+        assertEquals(link.toRedirectURI(), ENCODED);
+
+        // Make sure the link was passed to the LinkFactoryListener
+
+        assertSame(link, holder.get());
 
+        verify();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void train_triggerPassivateEventForPageLink(ComponentPageElement rootElement,
+            LinkFactoryListener listener, final Holder<Link> holder)
+    {
         IAnswer<Boolean> triggerEventAnswer = new IAnswer<Boolean>()
         {
             public Boolean answer() throws Throwable
@@ -235,37 +230,55 @@
 
         expect(
                 rootElement.triggerEvent(
-                        EasyMock.eq(TapestryConstants.PASSIVATE_EVENT),
-                        (Object[]) EasyMock.isNull(),
-                        EasyMock.isA(ComponentEventHandler.class))).andAnswer(triggerEventAnswer);
+                        eq(TapestryConstants.PASSIVATE_EVENT),
+                        (Object[]) isNull(),
+                        isA(ComponentEventHandler.class))).andAnswer(triggerEventAnswer);
 
-        train_encodeRedirectURL(
-                response,
-                "/barney/" + PAGE_LOGICAL_NAME.toLowerCase() + "/foo/bar",
-                ENCODED);
-
-        listener.createdPageLink(EasyMock.isA(Link.class));
+        listener.createdPageLink(isA(Link.class));
         getMocksControl().andAnswer(createdPageLinkAnswer);
+    }
 
-        // This needs to be refactored a bit to be more testable.
+    @SuppressWarnings("unchecked")
+    private void train_triggerPassivateEventForActionLink(ComponentPageElement rootElement,
+            LinkFactoryListener listener, final Holder<Link> holder)
+    {
+        IAnswer<Boolean> triggerEventAnswer = new IAnswer<Boolean>()
+        {
+            public Boolean answer() throws Throwable
+            {
+                ComponentEventHandler handler = (ComponentEventHandler) EasyMock
+                        .getCurrentArguments()[2];
 
-        map.store(isA(Link.class), isA(ComponentInvocation.class));
+                handler.handleResult(new Object[]
+                { "foo", "bar" }, null, null);
 
-        replay();
+                return true;
+            }
+        };
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, resolver, map, cache,
-                _typeCoercer);
-        factory.addListener(listener);
+        IAnswer<Void> createdPageLinkAnswer = new IAnswer<Void>()
+        {
+            public Void answer() throws Throwable
+            {
+                Link link = (Link) EasyMock.getCurrentArguments()[0];
 
-        Link link = factory.createPageLink(PAGE_LOGICAL_NAME);
+                holder.put(link);
 
-        assertEquals(link.toRedirectURI(), ENCODED);
+                return null;
+            }
+        };
 
-        // Make sure the link was passed to the LinkFactoryListener
+        // Intercept the call to handle component event, and let the IAnswer
+        // do the work.
 
-        assertSame(link, holder.get());
+        expect(
+                rootElement.triggerEvent(
+                        eq(TapestryConstants.PASSIVATE_EVENT),
+                        (Object[]) isNull(),
+                        isA(ComponentEventHandler.class))).andAnswer(triggerEventAnswer);
 
-        verify();
+        listener.createdActionLink(isA(Link.class));
+        getMocksControl().andAnswer(createdPageLinkAnswer);
     }
 
     @SuppressWarnings("unchecked")
@@ -277,41 +290,34 @@
         ComponentClassResolver resolver = newComponentClassResolver();
         ComponentPageElement element = newComponentPageElement();
         Page page = newPage();
+        ComponentPageElement rootElement = newComponentPageElement();
         LinkFactoryListener listener = newLinkFactoryListener();
         ComponentInvocationMap map = newComponentInvocationMap();
+        RequestPageCache cache = newRequestPageCache();
 
         final Holder<Link> holder = new Holder<Link>();
 
-        IAnswer<Void> createActionLinkAnswer = new IAnswer<Void>()
-        {
-            public Void answer() throws Throwable
-            {
-                Link link = (Link) EasyMock.getCurrentArguments()[0];
-
-                holder.put(link);
-
-                return null;
-            }
-        };
-
         train_getContainingPage(element, page);
         train_getName(page, pageClassName);
         train_resolvePageClassNameToPageName(resolver, pageClassName, logicalPageName);
         train_getContextPath(request, contextPath);
         train_getNestedId(element, nestedId);
 
-        listener.createdActionLink(EasyMock.isA(Link.class));
-        getMocksControl().andAnswer(createActionLinkAnswer);
+        train_getRootElement(page, rootElement);
+        train_triggerPassivateEventForActionLink(rootElement, listener, holder);
 
         // This needs to be refactored a bit to be more testable.
 
         map.store(isA(Link.class), isA(ComponentInvocation.class));
 
-        train_encodeURL(response, expectedURI, ENCODED);
+        train_encodeURL(response, String.format(
+                "%s?%s=foo/bar",
+                expectedURI,
+                InternalConstants.PAGE_CONTEXT_NAME), ENCODED);
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, resolver, map, null,
+        LinkFactory factory = new LinkFactoryImpl(request, response, resolver, map, cache,
                 _typeCoercer);
         factory.addListener(listener);
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java?view=diff&rev=510369&r1=510368&r2=510369
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java Wed Feb 21 19:26:14 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,133 +12,115 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import java.util.Arrays;
-
-import org.apache.tapestry.Link;
-import org.apache.tapestry.internal.test.InternalBaseTestCase;
-import org.apache.tapestry.services.Response;
-import org.testng.annotations.Test;
-
-public class LinkImplTest extends InternalBaseTestCase
-{
-    private static final String ENCODED = "*encoded*";
-
-    @Test
-    public void url_with_parameters()
-    {
-        Response response = newResponse();
-
-        train_encodeURL(response, "/foo/bar?barney=rubble&fred=flintstone", ENCODED);
-
-        replay();
-
-        Link link = new LinkImpl(response, "/foo", "bar");
-
-        link.addParameter("fred", "flintstone");
-        link.addParameter("barney", "rubble");
-
-        assertEquals(link.toURI(), ENCODED);
-
-        verify();
-    }
-
-    @Test
-    public void retrieve_parameter_values()
-    {
-        Response response = newResponse();
-
-        replay();
-
-        Link link = new LinkImpl(response, "/foo", "bar");
-
-        link.addParameter("fred", "flintstone");
-        link.addParameter("barney", "rubble");
-
-        assertEquals(link.getParameterValue("fred"), "flintstone");
-        assertEquals(link.getParameterValue("barney"), "rubble");
-        assertNull(link.getParameterValue("wilma"));
-
-        verify();
-    }
-
-    @Test
-    public void ensure_parameter_values_are_encoded()
-    {
-        Response response = newResponse();
-
-        train_encodeURL(response, "/foo/bar?fred=flint+stone%3F", ENCODED);
-
-        replay();
-
-        Link link = new LinkImpl(response, "/foo", "bar");
-
-        link.addParameter("fred", "flint stone?");
-
-        assertEquals(link.toURI(), ENCODED);
-
-        verify();
-    }
-
-    @Test
-    public void parameter_names_are_returned_sorted()
-    {
-        Response response = newResponse();
-
-        replay();
-
-        Link link = new LinkImpl(response, "/foo", "bar");
-
-        link.addParameter("fred", "flintstone");
-        link.addParameter("barney", "rubble");
-
-        assertEquals(link.getParameterNames(), Arrays.asList("barney", "fred"));
-
-        verify();
-    }
-
-    @Test
-    public void parameter_names_must_be_unique()
-    {
-        Response response = newResponse();
-
-        replay();
-
-        Link link = new LinkImpl(response, "/foo", "bar");
-
-        link.addParameter("fred", "flintstone");
-        try
-        {
-            link.addParameter("fred", "flintstone");
-            unreachable();
-        }
-        catch (IllegalArgumentException ex)
-        {
-            assertEquals(
-                    ex.getMessage(),
-                    "Parameter names are required to be unique.  Parameter 'fred' already has the value 'flintstone'.");
-        }
-
-        verify();
-    }
-
-    @Test
-    public void to_form_URI_does_not_include_parameters()
-    {
-        Response response = newResponse();
-
-        train_encodeURL(response, "/foo/bar", ENCODED);
-
-        replay();
-
-        Link link = new LinkImpl(response, "/foo", "bar", true);
-
-        link.addParameter("fred", "flintstone");
-        link.addParameter("barney", "rubble");
-
-        assertEquals(link.toURI(), ENCODED);
-
-        verify();
-    }
-}
+package org.apache.tapestry.internal.services;
+
+import java.util.Arrays;
+
+import org.apache.tapestry.Link;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.services.Response;
+import org.testng.annotations.Test;
+
+public class LinkImplTest extends InternalBaseTestCase
+{
+    private static final String ENCODED = "*encoded*";
+
+    @Test
+    public void url_with_parameters()
+    {
+        Response response = newResponse();
+
+        train_encodeURL(response, "/foo/bar?barney=rubble&fred=flintstone", ENCODED);
+
+        replay();
+
+        Link link = new LinkImpl(response, "/foo", "bar");
+
+        link.addParameter("fred", "flintstone");
+        link.addParameter("barney", "rubble");
+
+        assertEquals(link.toURI(), ENCODED);
+
+        verify();
+    }
+
+    @Test
+    public void retrieve_parameter_values()
+    {
+        Response response = newResponse();
+
+        replay();
+
+        Link link = new LinkImpl(response, "/foo", "bar");
+
+        link.addParameter("fred", "flintstone");
+        link.addParameter("barney", "rubble");
+
+        assertEquals(link.getParameterValue("fred"), "flintstone");
+        assertEquals(link.getParameterValue("barney"), "rubble");
+        assertNull(link.getParameterValue("wilma"));
+
+        verify();
+    }
+
+    @Test
+    public void parameter_names_are_returned_sorted()
+    {
+        Response response = newResponse();
+
+        replay();
+
+        Link link = new LinkImpl(response, "/foo", "bar");
+
+        link.addParameter("fred", "flintstone");
+        link.addParameter("barney", "rubble");
+
+        assertEquals(link.getParameterNames(), Arrays.asList("barney", "fred"));
+
+        verify();
+    }
+
+    @Test
+    public void parameter_names_must_be_unique()
+    {
+        Response response = newResponse();
+
+        replay();
+
+        Link link = new LinkImpl(response, "/foo", "bar");
+
+        link.addParameter("fred", "flintstone");
+        try
+        {
+            link.addParameter("fred", "flintstone");
+            unreachable();
+        }
+        catch (IllegalArgumentException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Parameter names are required to be unique.  Parameter 'fred' already has the value 'flintstone'.");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void to_form_URI_does_not_include_parameters()
+    {
+        Response response = newResponse();
+
+        train_encodeURL(response, "/foo/bar", ENCODED);
+
+        replay();
+
+        Link link = new LinkImpl(response, "/foo", "bar", true);
+
+        link.addParameter("fred", "flintstone");
+        link.addParameter("barney", "rubble");
+
+        assertEquals(link.toURI(), ENCODED);
+
+        verify();
+    }
+}