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 2008/01/08 19:18:41 UTC

svn commit: r610090 [1/2] - in /tapestry/tapestry5/trunk: tapestry-core/src/main/java/org/apache/tapestry/ tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/ tapestry-core/src/main/java/org/apache/tapestry/internal/services/ tapestry-core/...

Author: hlship
Date: Tue Jan  8 10:18:27 2008
New Revision: 610090

URL: http://svn.apache.org/viewvc?rev=610090&view=rev
Log:
TAPESTRY-1502: Generated URLs should be relative to request base URL

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/RequestPathOptimizerImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/util/TimeInterval.java
      - copied, changed from r605435, tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/util/TimePeriod.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/util/TimeIntervalTest.java
      - copied, changed from r605435, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/util/TimePeriodTest.java
Removed:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/util/TimePeriod.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/util/TimePeriodTest.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/Link.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/TapestryConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/Autocomplete.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ContextAssetFactory.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/PageTesterModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/LibraryMapping.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/RequestGlobals.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ServletApplicationInitializerFilter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/conf.apt
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/components/Border.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Localization.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ContextAssetFactoryTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/components/Border.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/Localization.tml
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/IntermediateType.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/util/UtilMessages.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/util/UtilStrings.properties
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/TypeCoercerImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/Link.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/Link.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/Link.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/Link.java Tue Jan  8 10:18:27 2008
@@ -58,7 +58,8 @@
 
     /**
      * Returns the URI portion of the link. When the link is created for a form, this will not
-     * include query parameters. This is the same value returned from toString().
+     * include query parameters. This is the same value returned from toString().  In some circumstances,
+     * this may be a relative URI (relative to the current Request's URI).
      *
      * @return the URI, ready to be added as an element attribute
      */
@@ -84,4 +85,11 @@
      */
     void setAnchor(String anchor);
 
+    /**
+     * Converts the link to a full URI, a complete path, starting with a leading slash. This is necessary
+     * in many cases for client-side JavaScript that must send a request to application via XMLHttpRequest.
+     *
+     * @return the complete URI (not abbreviated relative to the current request path)
+     */
+    String toFullURI();
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/TapestryConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/TapestryConstants.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/TapestryConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/TapestryConstants.java Tue Jan  8 10:18:27 2008
@@ -75,6 +75,12 @@
      */
     public static final String ERROR_CLASS = "t-error";
 
+
+    /**
+     * Symbol which may be set to "true" to force the use of full URIs (not relative URIs) exclusively.
+     */
+    public static final String FORCE_FULL_URIS_SYMBOL = "tapestry.force-full-uris";
+
     private TapestryConstants()
     {
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/Autocomplete.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/Autocomplete.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/Autocomplete.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/Autocomplete.java Tue Jan  8 10:18:27 2008
@@ -171,7 +171,8 @@
             }
         }
 
-        _pageRenderSupport.addScript("new Ajax.Autocompleter('%s', '%s', '%s', %s);", id, menuId, link, config);
+        _pageRenderSupport.addScript("new Ajax.Autocompleter('%s', '%s', '%s', %s);", id, menuId, link.toFullURI(),
+                                     config);
     }
 
     Object onAutocomplete()

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImpl.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImpl.java Tue Jan  8 10:18:27 2008
@@ -29,6 +29,8 @@
 {
     private final Request _request;
 
+    private final RequestPathOptimizer _optimizer;
+
     /**
      * Map from alias to path.
      */
@@ -49,9 +51,12 @@
      */
     public ClasspathAssetAliasManagerImpl(Request request,
 
+                                          RequestPathOptimizer optimizer,
+
                                           Map<String, String> configuration)
     {
         _request = request;
+        _optimizer = optimizer;
 
         for (Map.Entry<String, String> e : configuration.entrySet())
         {
@@ -87,6 +92,13 @@
 
     public String toClientURL(String resourcePath)
     {
+        String path = toCompleteClientURI(resourcePath);
+
+        return _optimizer.optimizePath(path);
+    }
+
+    private String toCompleteClientURI(String resourcePath)
+    {
         StringBuilder builder = new StringBuilder(_request.getContextPath());
         builder.append(TapestryConstants.ASSET_PATH_PREFIX);
 
@@ -117,8 +129,7 @@
         {
             if (basePath.startsWith(alias))
             {
-                return _aliasToPathPrefix.get(alias)
-                        + basePath.substring(alias.length());
+                return _aliasToPathPrefix.get(alias) + basePath.substring(alias.length());
             }
         }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ContextAssetFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ContextAssetFactory.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ContextAssetFactory.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ContextAssetFactory.java Tue Jan  8 10:18:27 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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,10 +31,13 @@
 
     private final Context _context;
 
-    public ContextAssetFactory(Request request, Context context)
+    private final RequestPathOptimizer _optimizer;
+
+    public ContextAssetFactory(Request request, Context context, RequestPathOptimizer optimizer)
     {
         _request = request;
         _context = context;
+        _optimizer = optimizer;
     }
 
     public Asset createAsset(final Resource resource)
@@ -50,7 +53,7 @@
 
             public String toClientURL()
             {
-                return contextPath;
+                return _optimizer.optimizePath(contextPath);
             }
 
             /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java Tue Jan  8 10:18:27 2008
@@ -16,7 +16,7 @@
 
 import org.apache.tapestry.ioc.annotations.IntermediateType;
 import org.apache.tapestry.ioc.annotations.Symbol;
-import org.apache.tapestry.ioc.util.TimePeriod;
+import org.apache.tapestry.ioc.util.TimeInterval;
 import org.apache.tapestry.services.Cookies;
 import org.apache.tapestry.services.Request;
 
@@ -47,7 +47,7 @@
 
                        CookieSink cookieSink,
 
-                       @Symbol("tapestry.default-cookie-max-age") @IntermediateType(TimePeriod.class)
+                       @Symbol("tapestry.default-cookie-max-age") @IntermediateType(TimeInterval.class)
                        long defaultMaxAge)
     {
         _request = request;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Tue Jan  8 10:18:27 2008
@@ -47,23 +47,36 @@
 
     private final TypeCoercer _typeCoercer;
 
+    private final RequestPathOptimizer _optimizer;
+
     private final List<LinkFactoryListener> _listeners = newThreadSafeList();
 
     private final StrategyRegistry<PassivateContextHandler> _registry;
 
+
     private interface PassivateContextHandler<T>
     {
         void handle(T result, List context);
     }
 
-    public LinkFactoryImpl(Request request, Response encoder, ComponentInvocationMap componentInvocationMap,
-                           RequestPageCache pageCache, TypeCoercer typeCoercer)
+    public LinkFactoryImpl(Request request,
+
+                           Response encoder,
+
+                           ComponentInvocationMap componentInvocationMap,
+
+                           RequestPageCache pageCache,
+
+                           TypeCoercer typeCoercer,
+
+                           RequestPathOptimizer optimizer)
     {
         _request = request;
         _response = encoder;
         _componentInvocationMap = componentInvocationMap;
         _pageCache = pageCache;
         _typeCoercer = typeCoercer;
+        _optimizer = optimizer;
 
         Map<Class, PassivateContextHandler> registrations = newMap();
 
@@ -118,7 +131,7 @@
 
         ComponentInvocation invocation = new ComponentInvocationImpl(target, contextStrings, activationContext);
 
-        Link link = new LinkImpl(_response, _request.getContextPath(), invocation, forForm);
+        Link link = new LinkImpl(_response, _optimizer, _request.getContextPath(), invocation, forForm);
 
         // Now see if the page has an activation context.
 
@@ -165,7 +178,7 @@
         PageLinkTarget target = new PageLinkTarget(logicalPageName);
         ComponentInvocation invocation = new ComponentInvocationImpl(target, context, null);
 
-        Link link = new LinkImpl(_response, _request.getContextPath(), invocation, false);
+        Link link = new LinkImpl(_response, _optimizer, _request.getContextPath(), invocation, false);
 
         _componentInvocationMap.store(link, invocation);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java Tue Jan  8 10:18:27 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.Link;
+import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.services.Response;
 
 import java.util.List;
@@ -24,31 +25,47 @@
  */
 public class LinkImpl implements Link
 {
-    private final Response _response;
+    private static final int BUFFER_SIZE = 100;
 
     private final String _contextPath;
 
+    private final Response _response;
+
+    private final RequestPathOptimizer _optimizer;
+
     private final ComponentInvocation _invocation;
 
     private final boolean _forForm;
 
     private String _anchor;
 
-    public LinkImpl(Response encoder, String contextPath, String targetPath)
+    LinkImpl(Response response, RequestPathOptimizer optimizer, String contextPath, String targetPath)
     {
-        this(encoder, contextPath, targetPath, false);
+        this(response, optimizer, contextPath, targetPath, false);
     }
 
-    public LinkImpl(Response encoder, String contextPath, String targetPath, boolean forForm)
+    LinkImpl(Response response, RequestPathOptimizer optimizer, String contextPath, String targetPath, boolean forForm)
     {
-        this(encoder, contextPath,
+        this(response, optimizer, contextPath,
              new ComponentInvocationImpl(new OpaqueConstantTarget(targetPath), new String[0], null), forForm);
     }
 
-    public LinkImpl(Response encoder, String contextPath, ComponentInvocation invocation, boolean forForm)
+    /**
+     * Creates a new Link.  Links may be full or optimized; optimization involves creating a relative URI from the
+     * request's URI to the Link's URI.
+     *
+     * @param response    used to encode the response when necessary
+     * @param optimizer   optimizes complete URLs to appropriate relative URLs
+     * @param contextPath path for the context {@link org.apache.tapestry.services.Request#getContextPath()}
+     * @param invocation  abstraction around the type of link (needed by {@link org.apache.tapestry.test.PageTester})
+     * @param forForm     if true, then a Form has requested the Link, in which case, the link should not generated query parameters directly (they will
+     */
+    public LinkImpl(Response response, RequestPathOptimizer optimizer, String contextPath,
+                    ComponentInvocation invocation, boolean forForm)
     {
+        _response = response;
+        _optimizer = optimizer;
         _contextPath = contextPath;
-        _response = encoder;
         _invocation = invocation;
         _forForm = forForm;
     }
@@ -70,26 +87,36 @@
 
     public String toURI()
     {
-        return _response.encodeURL(buildURI());
+        return _response.encodeURL(buildURI(false));
+    }
+
+    public String toFullURI()
+    {
+        return _response.encodeURL(buildURI(true));
     }
 
-    private String buildURI()
+
+    private String buildURI(boolean full)
     {
-        StringBuilder builder = new StringBuilder();
+        StringBuilder builder = new StringBuilder(BUFFER_SIZE);
         builder.append(_contextPath);
         builder.append("/");
         builder.append(_invocation.buildURI(_forForm));
-        if (_anchor != null && _anchor.length() > 0)
+
+        if (InternalUtils.isNonBlank(_anchor))
         {
             builder.append("#");
             builder.append(_anchor);
         }
-        return builder.toString();
+
+        String fullURI = builder.toString();
+
+        return full ? fullURI : _optimizer.optimizePath(fullURI);
     }
 
     public String toRedirectURI()
     {
-        return _response.encodeRedirectURL(buildURI());
+        return _response.encodeRedirectURL(buildURI(true));
     }
 
     public String getAnchor()
@@ -100,11 +127,6 @@
     public void setAnchor(String anchor)
     {
         _anchor = anchor;
-    }
-
-    public ComponentInvocation getInvocation()
-    {
-        return _invocation;
     }
 
     @Override

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizer.java?rev=610090&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizer.java Tue Jan  8 10:18:27 2008
@@ -0,0 +1,37 @@
+// Copyright 2008 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.internal.services;
+
+/**
+ * Used to optimize a path for inclusion in the rendered output of the page. When using lots of libraries,
+ * nested folders, and page acivation contexts, you can often create a shorter URL as a relative path
+ * from the current request URL.  Of course, you need to make sure that's turned off inside an Ajax request
+ * since the base URL of the client it totally unknown in that situation.
+ *
+ * @see org.apache.tapestry.TapestryConstants#FORCE_FULL_URIS_SYMBOL
+ */
+public interface RequestPathOptimizer
+{
+    /**
+     * Optimizes the provided path, returning a new path that is shorter but (combined with the current requests'
+     * base URL) will result in the same request URI.  In many cases, this will return the provided path unchanged.
+     * During {@linkplain org.apache.tapestry.services.Request#isXHR() XHR} requests, this will always
+     * return the provided path (no optimization takes place, since the base URI of the client is unknown).
+     *
+     * @param path to be optimized
+     * @return the same path, or a new path that is equivalent, relative to the current request's URL
+     */
+    String optimizePath(String path);
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizerImpl.java?rev=610090&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizerImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestPathOptimizerImpl.java Tue Jan  8 10:18:27 2008
@@ -0,0 +1,99 @@
+// Copyright 2008 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.internal.services;
+
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.ioc.annotations.Symbol;
+import org.apache.tapestry.services.Request;
+
+import java.util.regex.Pattern;
+
+public class RequestPathOptimizerImpl implements RequestPathOptimizer
+{
+    private final Request _request;
+
+    private final boolean _forceFull;
+
+    /**
+     * Used to split a URI up into individual folder/file names. Any number of consecutive slashes is treated as
+     * a single slash.
+     */
+    private final Pattern SLASH_PATTERN = Pattern.compile("/+");
+
+    public RequestPathOptimizerImpl(Request request,
+
+                                    @Symbol(TapestryConstants.FORCE_FULL_URIS_SYMBOL)
+                                    boolean forceFull)
+    {
+        _request = request;
+
+        _forceFull = forceFull;
+    }
+
+    public String optimizePath(String path)
+    {
+        if (_forceFull || _request.isXHR()) return path;
+
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(_request.getContextPath());
+        builder.append(_request.getPath());
+
+        String requestURI = builder.toString();
+
+        String[] requestTerms = SLASH_PATTERN.split(requestURI);
+        String[] pathTerms = SLASH_PATTERN.split(path);
+
+        builder.setLength(0);
+
+        int i = 0;
+        while (true)
+        {
+
+            if (i >= requestTerms.length - 1) break;
+
+            if (i >= pathTerms.length - 1) break;
+
+            if (!requestTerms[i].equals(pathTerms[i])) break;
+
+            i++;
+        }
+
+        // Found the point of divergence.
+
+        for (int j = i; j < requestTerms.length - 1; j++)
+        {
+            builder.append("../");
+        }
+
+        String sep = "";
+
+        for (int j = i; j < pathTerms.length; j++)
+        {
+            builder.append(sep);
+
+            builder.append(pathTerms[j]);
+
+            sep = "/";
+        }
+
+        if (builder.length() < path.length()) return builder.toString();
+
+        // The complete path is actually shorter than the relative path, so just return the complete
+        // path.
+
+        return path;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java Tue Jan  8 10:18:27 2008
@@ -1,17 +1,17 @@
-// Copyright 2006, 2007, 2008 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.
-
+// Copyright 2006, 2007, 2008 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.internal.structure;
 
 import org.apache.tapestry.Block;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Tue Jan  8 10:18:27 2008
@@ -557,4 +557,15 @@
     {
         return newMock(PageContentTypeAnalyzer.class);
     }
+
+
+    protected final RequestPathOptimizer mockRequestPathOptimizer()
+    {
+        return newMock(RequestPathOptimizer.class);
+    }
+
+    protected final void train_optimizePath(RequestPathOptimizer optimizer, String path, String optimizedPath)
+    {
+        expect(optimizer.optimizePath(path)).andReturn(optimizedPath);
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/PageTesterModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/PageTesterModule.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/PageTesterModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/PageTesterModule.java Tue Jan  8 10:18:27 2008
@@ -14,10 +14,12 @@
 
 package org.apache.tapestry.internal.test;
 
+import org.apache.tapestry.TapestryConstants;
 import org.apache.tapestry.internal.services.ComponentInvocationMap;
 import org.apache.tapestry.internal.services.CookieSink;
 import org.apache.tapestry.internal.services.CookieSource;
 import org.apache.tapestry.ioc.Configuration;
+import org.apache.tapestry.ioc.MappedConfiguration;
 import org.apache.tapestry.ioc.ObjectLocator;
 import org.apache.tapestry.ioc.ServiceBinder;
 import org.apache.tapestry.services.AliasContribution;
@@ -68,5 +70,10 @@
         AliasContribution<T> contribution = AliasContribution.create(serviceClass, TEST_MODE, service);
 
         configuration.add(contribution);
+    }
+
+    public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration)
+    {
+        configuration.add(TapestryConstants.FORCE_FULL_URIS_SYMBOL, "true");
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/LibraryMapping.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/LibraryMapping.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/LibraryMapping.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/LibraryMapping.java Tue Jan  8 10:18:27 2008
@@ -1,17 +1,17 @@
-// Copyright 2006 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.
-
+// Copyright 2006 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.services;
 
 /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java Tue Jan  8 10:18:27 2008
@@ -1,17 +1,17 @@
-// 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.
-// 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.
-
+// 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.
+// 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.services;
 
 import org.apache.tapestry.ContentType;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java Tue Jan  8 10:18:27 2008
@@ -1,17 +1,17 @@
-// Copyright 2006 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.
-
+// Copyright 2006 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.services;
 
 import java.util.Collection;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/RequestGlobals.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/RequestGlobals.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/RequestGlobals.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/RequestGlobals.java Tue Jan  8 10:18:27 2008
@@ -1,17 +1,17 @@
-// 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.
-// 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.
-
+// 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.
+// 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.services;
 
 import javax.servlet.http.HttpServletRequest;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ServletApplicationInitializerFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ServletApplicationInitializerFilter.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ServletApplicationInitializerFilter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ServletApplicationInitializerFilter.java Tue Jan  8 10:18:27 2008
@@ -1,17 +1,17 @@
-// Copyright 2006 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.
-
+// Copyright 2006 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.services;
 
 import javax.servlet.ServletContext;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java Tue Jan  8 10:18:27 2008
@@ -37,7 +37,7 @@
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 import org.apache.tapestry.ioc.services.*;
 import org.apache.tapestry.ioc.util.StrategyRegistry;
-import org.apache.tapestry.ioc.util.TimePeriod;
+import org.apache.tapestry.ioc.util.TimeInterval;
 import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.runtime.ComponentResourcesAware;
@@ -114,6 +114,7 @@
         binder.bind(AjaxPartialResponseRenderer.class, AjaxPartialResponseRendererImpl.class);
         binder.bind(PageContentTypeAnalyzer.class, PageContentTypeAnalyzerImpl.class);
         binder.bind(ResponseRenderer.class, ResponseRendererImpl.class);
+        binder.bind(RequestPathOptimizer.class, RequestPathOptimizerImpl.class);
     }
 
     public static Alias build(Logger logger,
@@ -477,11 +478,11 @@
                                          final RequestExceptionHandler exceptionHandler,
 
                                          // @Inject not needed because its a long, not a String
-                                         @Symbol("tapestry.file-check-interval") @IntermediateType(TimePeriod.class)
+                                         @Symbol("tapestry.file-check-interval") @IntermediateType(TimeInterval.class)
                                          long checkInterval,
 
                                          @Symbol("tapestry.file-check-update-timeout")
-                                         @IntermediateType(TimePeriod.class)
+                                         @IntermediateType(TimeInterval.class)
                                          long updateTimeout,
 
                                          LocalizationSetter localizationSetter)
@@ -1696,6 +1697,8 @@
 
         configuration.add("tapestry.default-stylesheet", "org/apache/tapestry/default.css");
 
+        configuration.add(TapestryConstants.FORCE_FULL_URIS_SYMBOL, "false");
+
 // This is designed to make it easy to keep synchronized with script.aculo.ous. As we
 // support a new version, we create a new folder, and update the path entry. We can then
 // delete the old version folder (or keep it around). This should be more manageable than
@@ -1804,9 +1807,7 @@
     }
 
     @Marker(ClasspathProvider.class)
-    public AssetFactory buildClasspathAssetFactory(ResourceCache resourceCache,
-
-                                                   ClasspathAssetAliasManager aliasManager)
+    public AssetFactory buildClasspathAssetFactory(ResourceCache resourceCache, ClasspathAssetAliasManager aliasManager)
     {
         ClasspathAssetFactory factory = new ClasspathAssetFactory(resourceCache, aliasManager);
 
@@ -1816,9 +1817,9 @@
     }
 
     @Marker(ContextProvider.class)
-    public AssetFactory buildContextAssetFactory(ApplicationGlobals globals)
+    public AssetFactory buildContextAssetFactory(ApplicationGlobals globals, RequestPathOptimizer optimizer)
     {
-        return new ContextAssetFactory(_request, globals.getContext());
+        return new ContextAssetFactory(_request, globals.getContext(), optimizer);
     }
 
     public CookieSink buildCookieSink()

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/conf.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/conf.apt?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/conf.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/conf.apt Tue Jan  8 10:18:27 2008
@@ -74,18 +74,25 @@
   
 * Configuration Symbols
 
-  Tapestry may also be configured via {{{http://tapestry.apache.org/tapestry5/tapestry-ioc/symbols.html}symbols}}.  A certain number of built-in services
+  Tapestry may also be configured via {{{../../tapestry-ioc/symbols.html}symbols}}.  A certain number of built-in services
   (some of which are not even public) are configured via symbols.  These symbols can be overridden
-  by contributing to the tapestry.ioc.ApplicationDefaults service configuration, or on the command line
+  by contributing to the ApplicationDefaults service configuration, or on the command line
   by defining JVM System Properties with the -D command line option.
+
+  These symbols are always defined in terms of strings, that are coerced to the appropriate type (a number,
+  a boolean, etc.).  Of special note are <time intervals>, which are specified in a
+  {{{../../apidocs/org/apache/tapestry/ioc/util/TimeInterval.html}particular format}}.
+
   
   [tapestry.default-cookie-max-age]
-    The default age, in seconds, that cookies created by Tapestry will be kept in the client web browser.
+    The default time period that cookies created by Tapestry will be kept in the client web browser.
     The dfault value is equal to one week. 
     
     Primarily, this is used with a cookie that exists 
     to track the preferred user locale.
 
+    The default is "7 d" (that is, seven days).
+
   [tapestry.default-stylesheet]
     The default stylesheet automatically injected into every rendered HTML page.  Many Tapestry components assume that
     this stylesheet is available.  All the classes defined in the stylesheet are prefixed with "t-". The exact contents
@@ -95,11 +102,25 @@
     The default is org/apache/tapestry/default.css, stored on the classpath.
   
   [tapestry.file-check-interval]
-    Time (in milliseconds) between file system checks. During a file system check, only a single thread is active (all others
+    Time period between file system checks. During a file system check, only a single thread is active (all others
     are blocked) and any files loaded are checked for changes (this is part of {{{reload.html}automatic component reloading}}).
 
-    The default is 1000 (one second),  and is usually overridden with a higher value in production (say, between one and five minutes).
-    
+    The default is "1 s" (one second),  and is usually overridden with a higher value in production (say, between one and five minutes).
+
+  [tapestry.file-check-update-timeout]
+    Time period that Tapestry will wait to obtain the exclusive lock needed for a file check. The default is "50 ms" (50 milliseconds).
+
+    If the exclusive lock can't be obtained in that amount of time, the request will proceeed normally (without the check),
+    but each successive request will attempt to get the lock and perform the check until successful.
+
+  [tapestry.force-full-uris]
+     A flag (true or false). When false (the default), Tapestry will attempt to optimize URLs that it generates, using
+     relative URLs when such URLs are shorter than complete URLs.  You will see some savings when you make generous
+     use of libraries, sub-folders and page activation contexts ... otherwise, no significant difference.
+
+     When true, all URLs will be complete URLs (including the
+     context path, and the complete path for the request).
+
   [tapestry.scriptaculous]
     The path to the embedded copy of {{{http://script.aculo.us/}script.aculo.us}} packaged with Tapestry. This value may be overridden
     to use a different version of the script.aculo.us library. Tapestry's default version is 1.7.0 (including Prototype 1.5.0).

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Tue Jan  8 10:18:27 2008
@@ -630,7 +630,9 @@
 
         clickAndWait("link=Rating");
 
-        assertText("//img[@id='rating:sort']/@src", "/assets/tapestry/corelib/components/sort-asc.png");
+        // The lack of a leading slash indicates that the path was optimized, see TAPESTRY-1502
+
+        assertText("//img[@id='rating:sort']/@src", "assets/tapestry/corelib/components/sort-asc.png");
         assertText("//img[@id='rating:sort']/@alt", "[Asc]");
 
         assertTextSeries("//tr[22]/td[%d]", 1, "Mona Lisa Overdrive", "Labyrinth", "Juno Reactor", "Dance", "31",
@@ -640,14 +642,14 @@
 
         clickAndWait("link=Rating");
 
-        assertText("//img[@id='rating:sort']/@src", "/assets/tapestry/corelib/components/sort-desc.png");
+        assertText("//img[@id='rating:sort']/@src", "assets/tapestry/corelib/components/sort-desc.png");
         assertText("//img[@id='rating:sort']/@alt", "[Desc]");
 
         assertTextSeries("//tr[1]/td[%d]", 1, "Hey Blondie", "Out from Out Where");
 
         clickAndWait("link=Title");
 
-        assertText("//img[@id='title:sort']/@src", "/assets/tapestry/corelib/components/sort-asc.png");
+        assertText("//img[@id='title:sort']/@src", "assets/tapestry/corelib/components/sort-asc.png");
         assertText("//img[@id='title:sort']/@alt", "[Asc]");
 
         clickAndWait("link=1");
@@ -778,8 +780,10 @@
     {
         start("Client Validation Demo");
 
-        assertTextSeries("//script[%d]/@src", 1, "/assets/scriptaculous/prototype.js",
-                         "/assets/scriptaculous/scriptaculous.js");
+        // The lack of a leading slash indicates that the path was optimized, see TAPESTRY-1502
+
+        assertTextSeries("//script[%d]/@src", 1, "assets/scriptaculous/prototype.js",
+                         "assets/scriptaculous/scriptaculous.js");
 
         clickAndWait("link=Clear Data");
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/components/Border.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/components/Border.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/components/Border.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/components/Border.java Tue Jan  8 10:18:27 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -15,6 +15,10 @@
 package org.apache.tapestry.integration.app1.components;
 
 import org.apache.tapestry.annotations.IncludeStylesheet;
+import org.apache.tapestry.ioc.annotations.Inject;
+import org.apache.tapestry.ioc.services.Builtin;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.services.ComponentLayer;
 
 /**
  * Here's a component with a template, including a t:body element.
@@ -22,5 +26,22 @@
 @IncludeStylesheet("context:css/app.css")
 public class Border
 {
+    @Inject
+    @Builtin
+    private ClassFactory _iocClassFactory;
+
+    @Inject
+    @ComponentLayer
+    private ClassFactory _componentClassFactory;
+
+    public ClassFactory getComponentClassFactory()
+    {
+        return _componentClassFactory;
+    }
+
+    public ClassFactory getIocClassFactory()
+    {
+        return _iocClassFactory;
+    }
 
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Localization.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Localization.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Localization.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Localization.java Tue Jan  8 10:18:27 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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,9 +16,6 @@
 
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.annotations.Inject;
-import org.apache.tapestry.ioc.services.Builtin;
-import org.apache.tapestry.ioc.services.ClassFactory;
-import org.apache.tapestry.services.ComponentLayer;
 import org.apache.tapestry.services.PersistentLocale;
 import org.apache.tapestry.services.Request;
 
@@ -29,13 +26,6 @@
     @Inject
     private Messages _messages;
 
-    @Inject
-    @Builtin
-    private ClassFactory _iocClassFactory;
-
-    @Inject
-    @ComponentLayer
-    private ClassFactory _componentClassFactory;
 
     @Inject
     private Locale _locale;
@@ -61,15 +51,6 @@
         return _messages.get("via-inject");
     }
 
-    public ClassFactory getComponentClassFactory()
-    {
-        return _componentClassFactory;
-    }
-
-    public ClassFactory getIocClassFactory()
-    {
-        return _iocClassFactory;
-    }
 
     public void onActionFromFrench()
     {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImplTest.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClasspathAssetAliasManagerImplTest.java Tue Jan  8 10:18:27 2008
@@ -38,20 +38,24 @@
         return configuration;
     }
 
+    private static final String OPTIMIZED = "/opt/path";
+
     @Test(dataProvider = "to_client_url_data")
     public void to_client_url(String resourcePath, String expectedClientURL)
     {
         Request request = mockRequest();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
 
         train_getContextPath(request, "/ctx");
 
+        train_optimizePath(optimizer, "/ctx" + TapestryConstants.ASSET_PATH_PREFIX + expectedClientURL, OPTIMIZED);
+
+
         replay();
 
-        ClasspathAssetAliasManager manager = new ClasspathAssetAliasManagerImpl(request,
-                                                                                configuration());
+        ClasspathAssetAliasManager manager = new ClasspathAssetAliasManagerImpl(request, optimizer, configuration());
 
-        assertEquals(manager.toClientURL(resourcePath), "/ctx"
-                + TapestryConstants.ASSET_PATH_PREFIX + expectedClientURL);
+        assertEquals(manager.toClientURL(resourcePath), OPTIMIZED);
 
         verify();
     }
@@ -59,20 +63,16 @@
     @DataProvider(name = "to_client_url_data")
     public Object[][] to_client_url_data()
     {
-        return new Object[][]
-                {
-                        {"foo/bar/Baz.txt", "foo/bar/Baz.txt"},
-                        {"com/example/mylib/Foo.bar", "mylib/Foo.bar"},
-                        {"com/example/mylib/nested/Foo.bar", "mylib/nested/Foo.bar"},
-                        {"org/apache/tapestry/internal/Foo.bar", "tapestry-internal/Foo.bar"},
-                        {"org/apache/tapestry/Foo.bar", "tapestry/Foo.bar"},};
+        return new Object[][]{{"foo/bar/Baz.txt", "foo/bar/Baz.txt"}, {"com/example/mylib/Foo.bar", "mylib/Foo.bar"},
+                              {"com/example/mylib/nested/Foo.bar", "mylib/nested/Foo.bar"},
+                              {"org/apache/tapestry/internal/Foo.bar", "tapestry-internal/Foo.bar"},
+                              {"org/apache/tapestry/Foo.bar", "tapestry/Foo.bar"},};
     }
 
     @Test(dataProvider = "to_resource_path_data")
     public void to_resource_path(String clientURL, String expectedResourcePath)
     {
-        ClasspathAssetAliasManager manager = new ClasspathAssetAliasManagerImpl(null,
-                                                                                configuration());
+        ClasspathAssetAliasManager manager = new ClasspathAssetAliasManagerImpl(null, null, configuration());
 
         assertEquals(manager.toResourcePath(clientURL), expectedResourcePath);
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ContextAssetFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ContextAssetFactoryTest.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ContextAssetFactoryTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ContextAssetFactoryTest.java Tue Jan  8 10:18:27 2008
@@ -24,6 +24,7 @@
 
 public class ContextAssetFactoryTest extends InternalBaseTestCase
 {
+
     @Test
     public void root_resource()
     {
@@ -32,7 +33,7 @@
 
         replay();
 
-        AssetFactory factory = new ContextAssetFactory(request, context);
+        AssetFactory factory = new ContextAssetFactory(request, context, null);
 
         assertEquals(factory.getRootResource().toString(), "context:/");
 
@@ -44,20 +45,28 @@
     {
         Context context = mockContext();
         Request request = mockRequest();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
 
         Resource r = new ContextResource(context, "foo/Bar.txt");
 
         train_getContextPath(request, "/context");
 
+        train_optimizePath(optimizer, "/context/foo/Bar.txt", "/opt/path1");
+        train_optimizePath(optimizer, "/context/foo/Bar.txt", "/opt/path2");
+
         replay();
 
-        AssetFactory factory = new ContextAssetFactory(request, context);
+        AssetFactory factory = new ContextAssetFactory(request, context, optimizer);
 
         Asset asset = factory.createAsset(r);
 
         assertSame(asset.getResource(), r);
-        assertEquals(asset.toClientURL(), "/context/foo/Bar.txt");
-        assertEquals(asset.toString(), asset.toClientURL());
+        assertEquals(asset.toClientURL(), "/opt/path1");
+
+        // In real life, toString() is the same as toClientURL(), but we're testing
+        // that the optimize method is getting called, basically.
+
+        assertEquals(asset.toString(), "/opt/path2");
 
         verify();
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java Tue Jan  8 10:18:27 2008
@@ -95,6 +95,7 @@
         ComponentPageElement rootElement = mockComponentPageElement();
         LinkFactoryListener listener = mockLinkFactoryListener();
         ComponentInvocationMap map = mockComponentInvocationMap();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
 
         train_getLogicalName(page, PAGE_LOGICAL_NAME);
         train_getContextPath(request, "/barney");
@@ -113,7 +114,8 @@
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, map, null, _typeCoercer);
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, null, _typeCoercer, optimizer);
+
         factory.addListener(listener);
 
         Link link = factory.createPageLink(page, false);
@@ -136,6 +138,7 @@
         Page page = mockPage();
         LinkFactoryListener listener = mockLinkFactoryListener();
         ComponentInvocationMap map = mockComponentInvocationMap();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
 
         train_getLogicalName(page, PAGE_LOGICAL_NAME);
         train_getContextPath(request, "/barney");
@@ -155,7 +158,7 @@
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, map, null, _typeCoercer);
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, null, _typeCoercer, optimizer);
         factory.addListener(listener);
 
         Link link = factory.createPageLink(page, false, "biff", "bazz");
@@ -178,6 +181,7 @@
         Page page = mockPage();
         LinkFactoryListener listener = mockLinkFactoryListener();
         ComponentInvocationMap map = mockComponentInvocationMap();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
 
         train_getLogicalName(page, PAGE_LOGICAL_NAME);
         train_getContextPath(request, "/barney");
@@ -197,7 +201,7 @@
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, map, null, _typeCoercer);
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, null, _typeCoercer, optimizer);
         factory.addListener(listener);
 
         Link link = factory.createPageLink(page, true);
@@ -221,6 +225,7 @@
         LinkFactoryListener listener = mockLinkFactoryListener();
         ComponentInvocationMap map = mockComponentInvocationMap();
         RequestPageCache cache = mockRequestPageCache();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
 
         train_get(cache, PAGE_LOGICAL_NAME, page);
 
@@ -242,7 +247,7 @@
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer);
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer, optimizer);
         factory.addListener(listener);
 
         Link link = factory.createPageLink(PAGE_LOGICAL_NAME, false);
@@ -334,12 +339,17 @@
         LinkFactoryListener listener = mockLinkFactoryListener();
         ComponentInvocationMap map = mockComponentInvocationMap();
         RequestPageCache cache = mockRequestPageCache();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+
+        String optimizedPath = "/optimized/path";
 
         final Holder<Link> holder = new Holder<Link>();
 
         train_getLogicalName(page, "mypage");
         train_getContextPath(request, "");
 
+        train_optimizePath(optimizer, "/mypage:myaction/1.2.3/4.5.6?t:ac=foo/bar", optimizedPath);
+
         train_getRootElement(page, rootElement);
         train_triggerPassivateEventForActionLink(rootElement, listener, holder);
 
@@ -347,11 +357,11 @@
 
         map.store(isA(Link.class), isA(ComponentInvocation.class));
 
-        train_encodeURL(response, "/mypage:myaction/1.2.3/4.5.6?t:ac=foo/bar", ENCODED);
+        train_encodeURL(response, "/optimized/path", ENCODED);
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer);
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer, optimizer);
         factory.addListener(listener);
 
         Link link = factory.createActionLink(page, null, "myaction", false, "1.2.3", "4.5.6");
@@ -375,12 +385,19 @@
         LinkFactoryListener listener = mockLinkFactoryListener();
         ComponentInvocationMap map = mockComponentInvocationMap();
         RequestPageCache cache = mockRequestPageCache();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+
+        String optimizedPath = "/optimized/path";
+
+        String generatedPath = String.format("%s?%s=foo/bar", expectedURI, InternalConstants.PAGE_CONTEXT_NAME);
 
         final Holder<Link> holder = new Holder<Link>();
 
         train_getLogicalName(page, logicalPageName);
         train_getContextPath(request, contextPath);
 
+        train_optimizePath(optimizer, generatedPath, optimizedPath);
+
         train_getRootElement(page, rootElement);
         train_triggerPassivateEventForActionLink(rootElement, listener, holder);
 
@@ -388,12 +405,11 @@
 
         map.store(isA(Link.class), isA(ComponentInvocationImpl.class));
 
-        train_encodeURL(response, String.format("%s?%s=foo/bar", expectedURI, InternalConstants.PAGE_CONTEXT_NAME),
-                        ENCODED);
+        train_encodeURL(response, optimizedPath, ENCODED);
 
         replay();
 
-        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer);
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer, optimizer);
         factory.addListener(listener);
 
         Link link = factory.createActionLink(page, nestedId, eventName, false, context);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkImplTest.java Tue Jan  8 10:18:27 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -23,59 +23,85 @@
 
 public class LinkImplTest extends InternalBaseTestCase
 {
+    private static final String OPTIMIZED = "/optimized/path";
+
     private static final String ENCODED = "*encoded*";
 
+
     @Test
-    public void url_with_parameters()
+    public void simple_redirect()
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
-        train_encodeURL(response, "/foo/bar?barney=rubble&fred=flintstone", ENCODED);
+        train_encodeRedirectURL(response, "/foo/bar", ENCODED);
 
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar");
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
 
-        link.addParameter("fred", "flintstone");
-        link.addParameter("barney", "rubble");
 
-        assertEquals(link.toURI(), ENCODED);
+        assertEquals(link.toRedirectURI(), ENCODED);
 
         verify();
     }
 
     @Test
-    public void retrieve_parameter_values()
+    public void to_string_same_as_to_uri()
+    {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+        Response response = mockResponse();
+
+        train_optimizePath(optimizer, "/foo/bar", OPTIMIZED);
+        train_encodeURL(response, OPTIMIZED, ENCODED);
+
+        replay();
+
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
+
+
+        assertEquals(link.toString(), ENCODED);
+
+        verify();
+    }
+
+    @Test
+    public void url_with_parameters()
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
+        train_optimizePath(optimizer, "/foo/bar?barney=rubble&fred=flintstone", OPTIMIZED);
+        train_encodeURL(response, OPTIMIZED, ENCODED);
+
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar");
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
 
         link.addParameter("fred", "flintstone");
         link.addParameter("barney", "rubble");
 
-        assertEquals(link.getParameterValue("fred"), "flintstone");
-        assertEquals(link.getParameterValue("barney"), "rubble");
-        assertNull(link.getParameterValue("wilma"));
+        assertEquals(link.toURI(), ENCODED);
 
         verify();
     }
 
     @Test
-    public void parameter_names_are_returned_sorted()
+    public void retrieve_parameter_values()
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar");
+        Link link = new LinkImpl(response, optimizer, "", "bar");
 
         link.addParameter("fred", "flintstone");
         link.addParameter("barney", "rubble");
 
-        assertEquals(link.getParameterNames(), Arrays.asList("barney", "fred"));
+        assertEquals(link.getParameterValue("fred"), "flintstone");
+        assertEquals(link.getParameterValue("barney"), "rubble");
+        assertNull(link.getParameterValue("wilma"));
 
         verify();
     }
@@ -83,11 +109,12 @@
     @Test
     public void parameter_names_must_be_unique()
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar");
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
 
         link.addParameter("fred", "flintstone");
         try
@@ -97,9 +124,8 @@
         }
         catch (IllegalArgumentException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Parameter names are required to be unique.  Parameter 'fred' already has the value 'flintstone'.");
+            assertEquals(ex.getMessage(),
+                         "Parameter names are required to be unique.  Parameter 'fred' already has the value 'flintstone'.");
         }
 
         verify();
@@ -108,13 +134,15 @@
     @Test
     public void to_form_URI_does_not_include_parameters()
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
-        train_encodeURL(response, "/foo/bar", ENCODED);
+        train_optimizePath(optimizer, "/foo/bar", OPTIMIZED);
+        train_encodeURL(response, OPTIMIZED, ENCODED);
 
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar", true);
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar", true);
 
         link.addParameter("fred", "flintstone");
         link.addParameter("barney", "rubble");
@@ -144,16 +172,22 @@
 
     private void url_with_anchor(String anchor, String url)
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
-        train_encodeURL(response, url, ENCODED);
+
+        train_optimizePath(optimizer, url, OPTIMIZED);
+        train_encodeURL(response, OPTIMIZED, ENCODED);
+
 
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar");
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
 
         link.setAnchor(anchor);
 
+        assertSame(link.getAnchor(), anchor);
+
         assertEquals(link.toURI(), ENCODED);
 
         verify();
@@ -162,13 +196,16 @@
     @Test
     public void url_with_anchor_and_parameters()
     {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
         Response response = mockResponse();
 
-        train_encodeURL(response, "/foo/bar?barney=rubble&fred=flintstone#wilma", ENCODED);
+
+        train_optimizePath(optimizer, "/foo/bar?barney=rubble&fred=flintstone#wilma", OPTIMIZED);
+        train_encodeURL(response, OPTIMIZED, ENCODED);
 
         replay();
 
-        Link link = new LinkImpl(response, "/foo", "bar");
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
 
         link.addParameter("fred", "flintstone");
         link.addParameter("barney", "rubble");
@@ -179,4 +216,44 @@
         verify();
     }
 
+
+    @Test
+    public void force_full_uri()
+    {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+        Response response = mockResponse();
+
+
+        train_encodeURL(response, "/ctx/foo", ENCODED);
+
+        replay();
+
+
+        Link link = new LinkImpl(response, optimizer, "/ctx",
+                                 new ComponentInvocationImpl(new OpaqueConstantTarget("foo"), new String[0], null),
+                                 false);
+
+        assertEquals(link.toFullURI(), ENCODED);
+
+
+        verify();
+    }
+
+    @Test
+    public void parameter_names_are_returned_sorted()
+    {
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+        Response response = mockResponse();
+
+        replay();
+
+        Link link = new LinkImpl(response, optimizer, "/foo", "bar");
+
+        link.addParameter("fred", "flintstone");
+        link.addParameter("barney", "rubble");
+
+        assertEquals(link.getParameterNames(), Arrays.asList("barney", "fred"));
+
+        verify();
+    }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/RequestPathOptimizerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/RequestPathOptimizerImplTest.java?rev=610090&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/RequestPathOptimizerImplTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/RequestPathOptimizerImplTest.java Tue Jan  8 10:18:27 2008
@@ -0,0 +1,93 @@
+// Copyright 2008 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.internal.services;
+
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.services.Request;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class RequestPathOptimizerImplTest extends InternalBaseTestCase
+{
+    @DataProvider(name = "uri_optimization")
+    public Object[][] uri_optimization_data()
+    {
+        return new Object[][]{
+
+                {"/context", "/foo/bar", "foo/baz", "baz"},
+
+                {"/context", "/foo/bar", "foo//baz", "baz"},
+
+                {"/context", "/foo//bar", "foo/baz", "baz"},
+
+                {"", "/foo/bar", "foo/baz", "baz"},
+
+                {"/reallylongcontexttoensureitisrelative", "/foo/bar/baz/biff", "gnip/gnop", "../../../gnip/gnop"},
+
+                {"", "/foo/bar/baz/biff/yepthisissolongthatabsoluteurlisshorter/dude", "gnip/gnop", "/gnip/gnop"},
+
+                {"", "/foo/bar", "/foo/bar/baz/bif", "bar/baz/bif"},
+
+                {"", "/foo/bar/baz/bif", "foo", "/foo"},
+
+                {"/ctx", "/foo/bar/baz/bif", "foo", "/ctx/foo"},
+
+                {"/anotherobnoxiouslylongcontextthatiwllforcerelative", "/foo/bar/baz/bif", "foo", "../../../foo"}
+
+        };
+    }
+
+    @Test(dataProvider = "uri_optimization")
+    public void uri_optimization(String contextPath, String requestPath, String path, String expectedURI)
+    {
+        Request request = mockRequest();
+
+        train_isXHR(request, false);
+
+        train_getContextPath(request, contextPath);
+        train_getPath(request, requestPath);
+
+
+        replay();
+
+        RequestPathOptimizer optimizer = new RequestPathOptimizerImpl(request, false);
+
+        assertEquals(optimizer.optimizePath(expectedURI), expectedURI);
+
+        verify();
+    }
+
+    @Test
+    public void force_full_is_a_pass_through()
+    {
+        Request request = mockRequest();
+        String path = "/some/path";
+
+        train_isXHR(request, true);
+
+        replay();
+
+        RequestPathOptimizer optimizer = new RequestPathOptimizerImpl(request, false);
+
+        assertSame(optimizer.optimizePath(path), path);
+
+        verify();
+    }
+
+    private void train_isXHR(Request request, boolean isXHR)
+    {
+        expect(request.isXHR()).andReturn(isXHR).atLeastOnce();
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/components/Border.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/components/Border.tml?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/components/Border.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/components/Border.tml Tue Jan  8 10:18:27 2008
@@ -1,27 +1,39 @@
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
     <head>
         <title>Tapestry Integration Test Application #1</title>
-        <style>
-A.selected
-{
-  font-size: large;
-  font-weight: bold;
-}
+        <style>
+            A.selected
+            {
+            font-size: large;
+            font-weight: bold;
+            }
         </style>
     </head>
     <body>
         <t:body/>
-        
-        
-        <p>
-            <a t:type="PageLink" page="Start">Back to index</a>
+
+        <hr/>
+
+        <p>
+            <a t:type="PageLink" page="Start">Back to index</a>
         </p>
-        
-      <p>
-        Currently on page: ${componentResources.pageName}
-      </p>
-        
+
+        <p>
+            Currently on page: ${componentResources.pageName}
+        </p>
+
+
+        <h3>Fabricated classes:</h3>
+
+        <dl>
+            <dt>IOC Layer: ${iocClassFactory.createdClassCount}</dt>
+            <dd>Service proxies and interceptors</dd>
+
+            <dt>Component Layer: ${componentClassFactory.createdClassCount}</dt>
+            <dd>For the most part, this is going to be prop: bindings</dd>
+        </dl>
+
     </body>
-    
-    
+
+
 </html>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/Localization.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/Localization.tml?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/Localization.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/Localization.tml Tue Jan  8 10:18:27 2008
@@ -3,40 +3,23 @@
 
 
     <p>Demonstrates a few ways that component message catalogs can be accessed in code and in the
-        template.</p>
+        template.
+    </p>
 
 
-    <p> Via injected Messages property: [${injectedMessage}] </p>
+    <p>Via injected Messages property: [${injectedMessage}]</p>
 
-    <p> Via message: binding prefix: [${message:via-prefix}]</p>
-    
-  <p> From Application Message Catalog: [${message:app-catalog-status}]</p>
-    
-    <p> Page locale: [${locale}]</p>
-    
-    <p> Request locale: [${request.locale}]</p>
-
-	<a t:type="ActionLink" t:id="french">French</a>
+    <p>Via message: binding prefix: [${message:via-prefix}]</p>
 
-	<a t:type="ActionLink" t:id="english">English</a>
-
-<hr/>
+    <p>From Application Message Catalog: [${message:app-catalog-status}]</p>
 
-    <h2>Fabricated classes:</h2>
+    <p>Page locale: [${locale}]</p>
 
-    <p> In addition to transforming component classes as they are loaded, Tapestry will create new
-        classes out of whole cloth. Although this has nothing to do with Localization, this was a
-        handy page to stick these values (because I was curious). </p>
+    <p>Request locale: [${request.locale}]</p>
 
+    <a t:type="ActionLink" t:id="french">French</a>
 
-    <dl>
-        <dt>IOC Layer: ${iocClassFactory.createdClassCount}</dt>
-        <dd> Proxies and interceptors. </dd>
-
-        <dt> Component Layer: ${componentClassFactory.createdClassCount} </dt>
-        <dd> For the most part, this is going to be prop: bindings. </dd>
-    </dl>
-
+    <a t:type="ActionLink" t:id="english">English</a>
 
 
 </html>

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/IntermediateType.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/IntermediateType.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/IntermediateType.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/IntermediateType.java Tue Jan  8 10:18:27 2008
@@ -20,7 +20,7 @@
 /**
  * Used to guide Tapestry when coercing from a raw type to a field or parameter type, by forcing
  * Tapestry to coerce to the intermediate type.  This was introduced to allow coercion from
- * string to a time period (in milliseconds) via {@link org.apache.tapestry.ioc.util.TimePeriod}.
+ * string to a time period (in milliseconds) via {@link org.apache.tapestry.ioc.util.TimeInterval}.
  *
  * @see org.apache.tapestry.ioc.annotations.Value
  * @see org.apache.tapestry.ioc.annotations.Symbol

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java?rev=610090&r1=610089&r2=610090&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java Tue Jan  8 10:18:27 2008
@@ -19,7 +19,7 @@
 import org.apache.tapestry.ioc.annotations.Marker;
 import org.apache.tapestry.ioc.annotations.Value;
 import org.apache.tapestry.ioc.internal.services.*;
-import org.apache.tapestry.ioc.util.TimePeriod;
+import org.apache.tapestry.ioc.util.TimeInterval;
 
 import java.io.File;
 import java.lang.reflect.Array;
@@ -127,8 +127,8 @@
      * <li>Null to BigDecimal (zero)</li>
      * <li>Null to BigInteger (zero)</li>
      * <li>String to File</li>
-     * <li>String to {@link org.apache.tapestry.ioc.util.TimePeriod}</li>
-     * <li>{@link org.apache.tapestry.ioc.util.TimePeriod} to Long</li>
+     * <li>String to {@link org.apache.tapestry.ioc.util.TimeInterval}</li>
+     * <li>{@link org.apache.tapestry.ioc.util.TimeInterval} to Long</li>
      * </ul>
      * <p/>
      * The coercion of String to Long, BigInteger, Double and BigDecimal causes some minor headaches
@@ -388,17 +388,17 @@
             }
         });
 
-        add(configuration, String.class, TimePeriod.class, new Coercion<String, TimePeriod>()
+        add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>()
         {
-            public TimePeriod coerce(String input)
+            public TimeInterval coerce(String input)
             {
-                return new TimePeriod(input);
+                return new TimeInterval(input);
             }
         });
 
-        add(configuration, TimePeriod.class, Long.class, new Coercion<TimePeriod, Long>()
+        add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>()
         {
-            public Long coerce(TimePeriod input)
+            public Long coerce(TimeInterval input)
             {
                 return input.milliseconds();
             }