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/02/26 03:15:49 UTC

svn commit: r631068 - in /tapestry/tapestry5/trunk: tapestry-core/src/main/java/org/apache/tapestry/internal/services/ tapestry-core/src/main/java/org/apache/tapestry/services/ tapestry-core/src/test/app3/ tapestry-core/src/test/java/org/apache/tapestr...

Author: hlship
Date: Mon Feb 25 18:15:43 2008
New Revision: 631068

URL: http://svn.apache.org/viewvc?rev=631068&view=rev
Log:
TAPESTRY-2197: Support for "index" pages in packages

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Index.java
      - copied, changed from r630031, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Start.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Index.java
      - copied, changed from r628959, tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Start.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Index.tml
      - copied, changed from r628959, tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Start.tml
Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Start.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Start.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Start.tml
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationImpl.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/PageLinkTarget.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PageRenderRequestParameters.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app3/Login.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.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/PageRenderDispatcherTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/InternalUtils.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/InternalUtilsTest.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/GameOver.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Guess.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/resources/log4j.properties
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Guess.tml
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/env.apt
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/first.apt
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms.apt
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/hilo.apt

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java Mon Feb 25 18:15:43 2008
@@ -115,7 +115,15 @@
         {
             String prefix = mapping.getPathPrefix();
 
-            // TODO: Check that prefix is well formed (no leading or trailing slash)
+            while (prefix.startsWith("/"))
+            {
+                prefix = prefix.substring(1);
+            }
+
+            while (prefix.endsWith("/"))
+            {
+                prefix = prefix.substring(0, prefix.length() - 1);
+            }
 
             String rootPackage = mapping.getRootPackage();
 
@@ -261,6 +269,8 @@
         {
             String className = core.get(name);
 
+            if (name.equals("")) name = "(blank)";
+
             f.format(formatString, name, className);
         }
 
@@ -292,6 +302,20 @@
 
             if (isPage)
             {
+                int lastSlashx = logicalName.lastIndexOf("/");
+
+                String lastTerm = lastSlashx < 0 ? logicalName : logicalName.substring(lastSlashx + 1);
+
+                if (lastTerm.equalsIgnoreCase("index"))
+                {
+                    String reducedName = lastSlashx < 0 ? "" : logicalName.substring(0, lastSlashx);
+
+                    // Make the super-stripped name another alias to the class.
+
+                    logicalNameToClassName.put(reducedName, name);
+                    _pageNameToCanonicalPageName.put(reducedName, logicalName);
+                }
+
                 _pageClassNameToLogicalName.put(name, logicalName);
                 _pageNameToCanonicalPageName.put(logicalName, logicalName);
                 _pageNameToCanonicalPageName.put(unstrippedName, logicalName);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationImpl.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationImpl.java Mon Feb 25 18:15:43 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.
@@ -24,9 +24,9 @@
 import java.util.Map;
 
 /**
- * Represents an invocation for a page or a component in the current application. This information
- * is extracted from incoming URLs for a running application (or created by the {@link PageTester}.
- * Each invocation may provide a context (Object[]) and parameters to the invocation target.
+ * Represents an invocation for a page or a component in the current application. This information is extracted from
+ * incoming URLs for a running application (or created by the {@link PageTester}. Each invocation may provide a context
+ * (Object[]) and parameters to the invocation target.
  */
 public class ComponentInvocationImpl implements ComponentInvocation
 {
@@ -40,12 +40,12 @@
 
     /**
      * @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, or contains the activation context when the
-     *                          invocation is for a page render request
-     * @param activationContext page activation context for the page containing the component, supplied via a
-     *                          passivate event to the page's root component (used when an action component
-     *                          invocation is for a page with an activation context)
+     * @param context           context information supplied by the component to be provided back when the event on the
+     *                          component is triggered, or contains the activation context when the invocation is for a
+     *                          page render request
+     * @param activationContext page activation context for the page containing the component, supplied via a passivate
+     *                          event to the page's root component (used when an action component invocation is for a
+     *                          page with an activation context)
      */
     public ComponentInvocationImpl(InvocationTarget target, String[] context, String[] activationContext)
     {
@@ -86,8 +86,7 @@
     }
 
     /**
-     * @return Just like the return value of {@link #buildURI(boolean)} except that parameters are
-     *         not included.
+     * @return Just like the return value of {@link #buildURI(boolean)} except that parameters are not included.
      */
     private String getPath()
     {
@@ -96,7 +95,7 @@
 
         for (String id : _context)
         {
-            builder.append("/");
+            if (builder.length() > 0) builder.append("/");
 
             builder.append(TapestryInternalUtils.encodeContext(id));
         }

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=631068&r1=631067&r2=631068&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 Mon Feb 25 18:15:43 2008
@@ -189,7 +189,21 @@
         String[] context = (override || activationContext.length != 0) ? toContextStrings(
                 activationContext) : collectActivationContextForPage(page);
 
+        // Strip a trailing "/index" from the path.
+
+        int lastSlashx = logicalPageName.lastIndexOf("/");
+
+        String lastTerm = lastSlashx < 0 ? logicalPageName : logicalPageName.substring(lastSlashx + 1);
+
+        // This, alas, duplicates some logic inside ComponentClassResolverImpl ...
+
+        if (lastTerm.equalsIgnoreCase("index"))
+        {
+            logicalPageName = lastSlashx < 0 ? "" : logicalPageName.substring(0, lastSlashx);
+        }
+
         PageLinkTarget target = new PageLinkTarget(logicalPageName);
+
         ComponentInvocation invocation = new ComponentInvocationImpl(target, context, null);
 
         String baseURL = _requestSecurityManager.getBaseURL(page);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java Mon Feb 25 18:15:43 2008
@@ -22,7 +22,6 @@
  */
 public class PageLinkTarget implements InvocationTarget
 {
-
     private final String _pageName;
 
     public PageLinkTarget(String pageName)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java Mon Feb 25 18:15:43 2008
@@ -766,7 +766,7 @@
         // Controls how attributes are interpretted.
         _addAttributesAsComponentBindings = false;
 
-        // Start will be matched by end:
+        // Index will be matched by end:
 
         // Do NOT discard the end tag; add it to the body.
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java Mon Feb 25 18:15:43 2008
@@ -64,7 +64,7 @@
 
             pageName = path.substring(1, nextslashx);
 
-            if (_componentClassResolver.isPageName(pageName)) break;
+            if (!pageName.endsWith("/") && _componentClassResolver.isPageName(pageName)) break;
 
             nextslashx = path.lastIndexOf('/', nextslashx - 1);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PageRenderRequestParameters.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PageRenderRequestParameters.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PageRenderRequestParameters.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PageRenderRequestParameters.java Mon Feb 25 18:15:43 2008
@@ -18,9 +18,9 @@
 import org.apache.tapestry.ioc.internal.util.Defense;
 
 /**
- * Used with {@link org.apache.tapestry.services.PageRenderRequestHandler} and
- * {@link org.apache.tapestry.services.PageRenderRequestFilter} to define
- * the logical page name and activation context for the request.
+ * Used with {@link org.apache.tapestry.services.PageRenderRequestHandler} and {@link
+ * org.apache.tapestry.services.PageRenderRequestFilter} to define the logical page name and activation context for the
+ * request.
  */
 public class PageRenderRequestParameters
 {
@@ -30,7 +30,7 @@
 
     public PageRenderRequestParameters(String logicalPageName, EventContext activationContext)
     {
-        Defense.notBlank(logicalPageName, "logicalPageName");
+        Defense.notNull(logicalPageName, "logicalPageName");
         Defense.notNull(activationContext, "activationContext");
 
         _logicalPageName = logicalPageName;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app3/Login.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app3/Login.tml?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app3/Login.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app3/Login.tml Mon Feb 25 18:15:43 2008
@@ -6,7 +6,7 @@
         <h1>Login Page</h1>
 
         <p>
-            You have reached this page via a redirect from the Start page's onActivate() event handler method.
+            You have reached this page via a redirect from the Index page's onActivate() event handler method.
         </p>
     </body>
 </html>

Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Index.java (from r630031, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Start.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Index.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Index.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Start.java&r1=630031&r2=631068&rev=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Start.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app3/pages/Index.java Mon Feb 25 18:15:43 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 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,7 +16,7 @@
 
 import org.apache.tapestry.annotations.InjectPage;
 
-public class Start
+public class Index
 {
     @InjectPage
     private Login _login;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java Mon Feb 25 18:15:43 2008
@@ -278,6 +278,29 @@
     }
 
     @Test
+    public void index_page_name_at_root()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ClassNameLocator locator = newClassNameLocator();
+        Logger logger = compliantLogger();
+
+        train_for_app_packages(source);
+
+        String className = APP_ROOT_PACKAGE + ".pages.Index";
+
+        train_locateComponentClassNames(locator, APP_ROOT_PACKAGE + ".pages", className);
+
+        replay();
+
+        ComponentClassResolver resolver = create(logger, source, locator);
+
+        assertTrue(resolver.isPageName("Index"));
+        assertTrue(resolver.isPageName(""));
+
+        verify();
+    }
+
+    @Test
     public void is_page_name_for_core_page()
     {
         ComponentInstantiatorSource source = mockComponentInstantiatorSource();
@@ -466,6 +489,32 @@
 
         ComponentClassResolver resolver = create(logger, source, locator,
                                                  new LibraryMapping(LIB_PREFIX, LIB_ROOT_PACKAGE),
+                                                 new LibraryMapping(CORE_PREFIX, CORE_ROOT_PACKAGE));
+
+        assertEquals(resolver.resolvePageNameToClassName("lib/MyLibPage"), className);
+
+        verify();
+    }
+
+    @Test
+    public void slashes_trimmed_from_library_prefix()
+    {
+        String className = LIB_ROOT_PACKAGE + ".pages.MyLibPage";
+
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ClassNameLocator locator = newClassNameLocator();
+        Logger logger = compliantLogger();
+
+        train_for_packages(source, LIB_ROOT_PACKAGE);
+        train_for_packages(source, CORE_ROOT_PACKAGE);
+        train_for_app_packages(source);
+
+        train_locateComponentClassNames(locator, LIB_ROOT_PACKAGE + ".pages", className);
+
+        replay();
+
+        ComponentClassResolver resolver = create(logger, source, locator,
+                                                 new LibraryMapping("/" + LIB_PREFIX + "/", LIB_ROOT_PACKAGE),
                                                  new LibraryMapping(CORE_PREFIX, CORE_ROOT_PACKAGE));
 
         assertEquals(resolver.resolvePageNameToClassName("lib/MyLibPage"), className);

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=631068&r1=631067&r2=631068&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 Mon Feb 25 18:15:43 2008
@@ -332,6 +332,110 @@
         verify();
     }
 
+    @Test
+    public void page_link_for_root_index()
+    {
+        Request request = mockRequest();
+        Response response = mockResponse();
+        Page page = mockPage();
+        ComponentPageElement rootElement = mockComponentPageElement();
+        LinkFactoryListener listener = mockLinkFactoryListener();
+        ComponentInvocationMap map = mockComponentInvocationMap();
+        RequestPageCache cache = mockRequestPageCache();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+        RequestSecurityManager securityManager = mockRequestSecurityManager();
+
+        String logicalName = "Index";
+
+        train_get(cache, logicalName, page);
+
+        train_getLogicalName(page, logicalName);
+        train_getContextPath(request, "/barney");
+
+        train_getRootElement(page, rootElement);
+
+        // Answer for triggerEvent() which returns a boolean.
+        final Holder<Link> holder = new Holder<Link>();
+
+        train_triggerPassivateEventForPageLink(rootElement, listener, holder);
+
+        train_getBaseURL(securityManager, page, null);
+
+        train_encodeRedirectURL(response, "/barney/foo/bar", ENCODED);
+
+        // This needs to be refactored a bit to be more testable.
+
+        map.store(isA(Link.class), isA(ComponentInvocationImpl.class));
+
+        replay();
+
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, optimizer, null, _contextValueEncoder,
+                                                  securityManager);
+        factory.addListener(listener);
+
+        Link link = factory.createPageLink(logicalName, false);
+
+        assertEquals(link.toRedirectURI(), ENCODED);
+
+        // Make sure the link was passed to the LinkFactoryListener
+
+        assertSame(link, holder.get());
+
+        verify();
+    }
+
+    @Test
+    public void page_link_for_index_in_folder()
+    {
+        Request request = mockRequest();
+        Response response = mockResponse();
+        Page page = mockPage();
+        ComponentPageElement rootElement = mockComponentPageElement();
+        LinkFactoryListener listener = mockLinkFactoryListener();
+        ComponentInvocationMap map = mockComponentInvocationMap();
+        RequestPageCache cache = mockRequestPageCache();
+        RequestPathOptimizer optimizer = mockRequestPathOptimizer();
+        RequestSecurityManager securityManager = mockRequestSecurityManager();
+
+        String logicalName = "mylib/stuff/Index";
+
+        train_get(cache, logicalName, page);
+
+        train_getLogicalName(page, logicalName);
+        train_getContextPath(request, "/barney");
+
+        train_getRootElement(page, rootElement);
+
+        // Answer for triggerEvent() which returns a boolean.
+        final Holder<Link> holder = new Holder<Link>();
+
+        train_triggerPassivateEventForPageLink(rootElement, listener, holder);
+
+        train_getBaseURL(securityManager, page, null);
+
+        train_encodeRedirectURL(response, "/barney/mylib/stuff/foo/bar", ENCODED);
+
+        // This needs to be refactored a bit to be more testable.
+
+        map.store(isA(Link.class), isA(ComponentInvocationImpl.class));
+
+        replay();
+
+        LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, optimizer, null, _contextValueEncoder,
+                                                  securityManager);
+        factory.addListener(listener);
+
+        Link link = factory.createPageLink(logicalName, false);
+
+        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,
                                                         Holder<Link> holder)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java Mon Feb 25 18:15:43 2008
@@ -106,9 +106,6 @@
         String path = "/foo/Bar" + (finalSlash ? "/" : "");
         train_getPath(request, path);
 
-        if (finalSlash)
-            train_isPageName(resolver, path.substring(1), false);
-
         train_isPageName(resolver, "foo/Bar", true);
 
         train_get(cache, "foo/Bar", page);
@@ -155,10 +152,7 @@
         String path = "/foo/Bar/zip/zoom" + (finalSlash ? "/" : "");
         train_getPath(request, path);
 
-        train_isPageName(resolver, path.substring(1), false);
-
-        if (finalSlash)
-            train_isPageName(resolver, "foo/Bar/zip/zoom", false);
+        train_isPageName(resolver, "foo/Bar/zip/zoom", false);
 
         train_isPageName(resolver, "foo/Bar/zip", false);
 

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/InternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/InternalUtils.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/InternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/InternalUtils.java Mon Feb 25 18:15:43 2008
@@ -252,7 +252,8 @@
     }
 
     /**
-     * Joins together some number of elements.
+     * Joins together some number of elements. If a value in the list is the empty string, it is replaced with the
+     * string "(blank)".
      *
      * @param elements  objects to be joined together
      * @param separator used between elements when joining
@@ -276,7 +277,11 @@
                 {
                     if (!first) buffer.append(separator);
 
-                    buffer.append(String.valueOf(o));
+                    String string = String.valueOf(o);
+
+                    if (string.equals("")) string = "(blank)";
+
+                    buffer.append(string);
 
                     first = false;
                 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/InternalUtilsTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/InternalUtilsTest.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/InternalUtilsTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/InternalUtilsTest.java Mon Feb 25 18:15:43 2008
@@ -133,6 +133,14 @@
     }
 
     @Test
+    public void join_with_blank()
+    {
+        List<String> many = Arrays.asList("fred", "barney", "", "wilma");
+        assertEquals(InternalUtils.join(many), "fred, barney, (blank), wilma");
+
+    }
+
+    @Test
     public void join_sorted()
     {
         List<String> unsorted = Arrays.asList("betty", "fred", "barney", "wilma");
@@ -143,6 +151,15 @@
         // Make sure that joinSorted() doesn't change the input list
 
         assertEquals(copy, unsorted);
+    }
+
+    @Test
+    public void join_sorted_with_blank()
+    {
+        List<String> unsorted = Arrays.asList("betty", "fred", "barney", "", "wilma");
+
+        assertEquals(InternalUtils.joinSorted(unsorted), "(blank), barney, betty, fred, wilma");
+
     }
 
     @Test(dataProvider = "capitalize_inputs")

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/GameOver.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/GameOver.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/GameOver.java (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/GameOver.java Mon Feb 25 18:15:43 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 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.
@@ -26,8 +26,10 @@
         return _count;
     }
 
-    void setup(int count)
+    Object initialize(int count)
     {
         _count = count;
+
+        return this;
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Guess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Guess.java?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Guess.java (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Guess.java Mon Feb 25 18:15:43 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 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.
@@ -42,11 +42,7 @@
     {
         _count++;
 
-        if (guess == _target)
-        {
-            _gameOver.setup(_count);
-            return _gameOver;
-        }
+        if (guess == _target) return _gameOver.initialize(_count);
 
         if (guess < _target)
             _message = String.format("%d is too low.", guess);
@@ -66,10 +62,12 @@
         _guess = guess;
     }
 
-    void setup(int target)
+    Object initialize(int target)
     {
         _target = target;
         _count = 0;
+
+        return this;
     }
 
     public int getTarget()

Copied: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Index.java (from r628959, tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Start.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Index.java?p2=tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Index.java&p1=tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Start.java&r1=628959&r2=631068&rev=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Start.java (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry/tutorial/pages/Index.java Mon Feb 25 18:15:43 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 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.
@@ -18,7 +18,7 @@
 
 import java.util.Random;
 
-public class Start
+public class Index
 {
     private final Random _random = new Random();
 
@@ -29,8 +29,6 @@
     {
         int target = _random.nextInt(10) + 1;
 
-        _guess.setup(target);
-
-        return _guess;
+        return _guess.initialize(target);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/resources/log4j.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/resources/log4j.properties?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/resources/log4j.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/resources/log4j.properties Mon Feb 25 18:15:43 2008
@@ -1,4 +1,4 @@
-# Copyright 2007 The Apache Software Foundation
+# Copyright 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.
@@ -32,4 +32,4 @@
 # Turning on debug mode for a page or component will show all of the code changes that occur when the
 # class is loaded.  Turning on debug mode for a page will enable verbose output about rendering
 # the page (and its components).
-# log4j.category.org.apache.tapestry.tutorial.pages.Start=debug
+# log4j.category.org.apache.tapestry.tutorial.pages.Index=debug

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Guess.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Guess.tml?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Guess.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Guess.tml Mon Feb 25 18:15:43 2008
@@ -1,16 +1,15 @@
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-  <head>
-    <title>Guess A Number</title>
-  </head>
-  <body>
+    <head>
+        <title>Guess A Number</title>
+    </head>
+    <body>
 
-  <p>Make a guess between one and ten:</p>
-  
-  <p>${message}</p>
-  
-    <t:loop source="1..10" value="guess">
-      <t:actionlink t:id="link" context="guess">${guess}</t:actionlink>
-    </t:loop>
+        <p>Make a guess between one and ten:</p>
 
-  </body>
+        <p>${message}</p>
+
+        <t:loop source="1..10" value="guess" xml:space="preserve">
+            <t:actionlink t:id="link" context="guess">${guess}</t:actionlink>
+        </t:loop>
+    </body>
 </html>

Copied: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Index.tml (from r628959, tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Start.tml)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Index.tml?p2=tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Index.tml&p1=tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Start.tml&r1=628959&r2=631068&rev=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Start.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/webapp/Index.tml Mon Feb 25 18:15:43 2008
@@ -1,22 +1,24 @@
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-  <head>
-    <title>tutorial1 Start Page</title>
-  </head>
-  <body>
+    <head>
+        <title>tutorial1 Start Page</title>
+    </head>
+    <body>
 
-    <h1>Hi/Lo Guess</h1>
+        <h1>Hi/Lo Guess</h1>
 
-    <p>I'm thinking of a number between one and ten ... </p>
+        <p>I'm thinking of a number between one and ten ...</p>
 
-    <p>
-      <t:actionlink>Start guessing</t:actionlink>
-    </p>
+        <p>
+            <t:actionlink>Start guessing</t:actionlink>
+        </p>
 
-    <h1>Address Book</h1>
+        <h1>Address Book</h1>
 
-    <ul>
-      <li><t:pagelink page="address/create">Create new address</t:pagelink></li>
-    </ul>
+        <ul>
+            <li>
+                <t:pagelink page="address/create">Create new address</t:pagelink>
+            </li>
+        </ul>
 
-  </body>
+    </body>
 </html>

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/env.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/env.apt?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/env.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/env.apt Mon Feb 25 18:15:43 2008
@@ -38,13 +38,13 @@
 
   <<Caution: JettyLauncher is only compatible with Jetty 4 and Jetty 5. It does not work with Jetty 6.>>
   
-* Maven 2.0.7
+* Maven 2.0.8
 
   Maven is a software build tool of rather epic ambitions. It has a very sophisticated plugin system that allows it to do virtually anything, though compiling Java code, building WAR and JAR files, and creating reports and web sites are its forte.
   
   Perhaps the biggest advantage of Maven over, say, Ant, is that it can download project dependencies (such as the Tapestry JAR files, and the JAR files Tapestry itself depends on) automatically for you, from one of several central repositories.
   
-  We'll be using Maven to set up our Tapestry applications.  Maven 2.0.7 is available from {{{http://maven.apache.org/download.html}http://maven.apache.org/download.html}}.
+  We'll be using Maven to set up our Tapestry applications.  Maven 2.0.8 is available from {{{http://maven.apache.org/download.html}http://maven.apache.org/download.html}}.
   
 * Maven Plugin
 

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/first.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/first.apt?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/first.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/first.apt Mon Feb 25 18:15:43 2008
@@ -20,9 +20,8 @@
 mvn archetype:create
   -DarchetypeGroupId=org.apache.tapestry
   -DarchetypeArtifactId=quickstart
-  -DarchetypeVersion=5.0.5
   -DgroupId=org.apache.tapestry
-  -DartifactId=tapestry-tutorial1
+  -DartifactId=tutorial1
   -DpackageName=org.apache.tapestry.tutorial
 ----
 
@@ -55,7 +54,7 @@
 </settings>
 ----
 
-  Of course, adjust \<localRepository\> to match the correct path for your computer.  
+  Of course, adjust  the \<localRepository\> element to match the correct path for your computer.  
 
   One of the first things you can do is use Maven to run Jetty directly.
   
@@ -68,11 +67,7 @@
   Again, the first time, there's a dizzying number of downloads, but before you know it, the Jetty servlet container
   is up and running.
 
-
-
-  One
-  
-  You can open a web browser to {{{http://localhost:8080/tapestry-tutorial1/}http://localhost:8080/tapestry-tutorial1/}}
+  You can open a web browser to {{{http://localhost:8080/tutorial1/}http://localhost:8080/tutorial1/}}
   to see the running application:
   
 [startpage.png] Default Start page for Application
@@ -151,17 +146,17 @@
   which ones apply to Tapestry pages (or other resources). The net effect is that you don't have to maintain any additional configuration
   for Tapestry to operate, regardless of how many pages or components you add to your application.
   
-  Tapestry has a special case for a URL that specifies the host and the context ("/tutorial1" in this case) but nothing else ... it renders
-  the Start page of the application. So far, the Start page is the only page in the application. Let's see what it looks like.
-  
   Tapestry pages minimally consist of an ordinary Java class plus a component template file.
-  
+
+  In the root of your web application, a page named "Index" will be used for any request that specifies no additional
+  path after the context name.
+    
   Let's start with the template, which is stored in the webapp's WEB-INF folder.  Tapestry component templates are well-formed XML documents.  This means that you can use
   any available XML editor.  Templates may even have a DOCTYPE or an XML schema to validate the structure of the template.  <That is, your build process may use a tool to validate your
   templates.  At runtime, when Tapestry reads the template, it does not use a validating parser.> For the most part, the template looks like
   ordinary XHTML:
   
-  <<src/main/webapp/Start.tml:>>
+  <<src/main/webapp/Index.tml:>>
   
 ----
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
@@ -178,13 +173,13 @@
 
 
         <p>
-            [<t:pagelink t:page="Start">refresh</t:pagelink>]
+            [<t:pagelink t:page="Index">refresh</t:pagelink>]
         </p>
     </body>
 </html>
 ----
 
- The goal in Tapestry is for component templates, such as Start.html, to look as much as possibly like ordinary, static
+ The goal in Tapestry is for component templates, such as Index.tml, to look as much as possible like ordinary, static
  HTML files. <By static, we mean unchanging, as opposed to a dynamically generated Tapestry page.>  In fact, the expectation
  is that in many cases, the templates will start as static HTML files, created by a web developer and then be
  <instrumented> to act as live Tapestry pages.
@@ -206,14 +201,13 @@
   This is how Tapestry works; the Start page contains an <instance> of the PageLink component. The PageLink component is configured via its parameters, which controls what
   it does and how it behaves.
   
-  The URL that the PageLink component will render out is <<<http://localhost:8080/tutorial1/start>>>.  Tapestry is case insensitive
-  (<<<http://localhost:8080/tutorial1/START>>> would work just as well), and generates lower-case URLs because those are more visually pleasing. <The servlet container
-  is not so forgiving, and expects an exact match on the context name portion of the URL: "/tutorial1".>
+  The URL that the PageLink component will render out is <<<http://localhost:8080/tapestry-tutorial1/>>>.    In later examples,
+  when we link to pages besides "Index", the page name will be part of the URL.
   
-  Tapestry ignores case where ever it can. Inside the template, we configured the PageLink component's page parameter with the name of the page, "Start".  Here too we could be
-  inexact on case.  Feel free to use "start" if that works for you. 
+  Tapestry ignores case where ever it can. Inside the template, we configured the PageLink component's page parameter with the name of the page, "Index".  Here too we could be
+  inexact on case.  Feel free to use "index" if that works for you. 
   
-  <You do have to name your component template file, Start.html, with the exact same case as the component class name, Start. If you get the case wrong, it may work on some
+  <You do have to name your component template file, Index.html, with the exact same case as the component class name, Index. If you get the case wrong, it may work on some
   operating systems (such as Windows) and not on others (Mac OS X, Linux, and most others). This can be really vexing, as it is common to develop on Windows and deploy on Linux or
   Solaris, so be careful about case in this one area.>
   
@@ -221,9 +215,9 @@
   re-used to generate the HTML sent to the browser, which results in the updated time showing up in the web browser.
   
   The final piece of the puzzle is the Java class for the page.  Tapestry has very specific rules for where page classes go.  Remember the package name (configured inside web.xml)?  Tapestry adds a
-  sub-package, "pages", to it and the Java class goes there. Thus the full Java class name is org.apache.tapestry.tutorial.pages.Start.
+  sub-package, "pages", to it and the Java class goes there. Thus the full Java class name is org.apache.tapestry.tutorial.pages.Index.
   
-  <<src/main/java/org/apache/tapestry/tutorial/pages/Start.java>>
+  <<src/main/java/org/apache/tapestry/tutorial/pages/Index.java>>
 
 ---
 package org.apache.tapestry.tutorial.pages;
@@ -233,7 +227,7 @@
 /**
  * Start page of application tutorial1.
  */
-public class Start
+public class Index
 {
   public Date getCurrentTime()
   {
@@ -298,7 +292,7 @@
   
   You may now start the application with the URL {{{http://localhost:8080/tutorial1/}http://localhost:8080/tutorial1/}}.
   
-  Now it's time for the magic trick. Edit Start.java and change the getCurrentTime() method to:
+  Now it's time for the magic trick. Edit Index.java and change the getCurrentTime() method to:
   
 ---
   public String getCurrentTime()

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms.apt?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms.apt Mon Feb 25 18:15:43 2008
@@ -162,9 +162,9 @@
   We'll create a sub-folder, address, to hold them.  Let's get started on the first of these pages, "address/Create"  (that's the real name, including
   the slash --- we'll see in a minute how that maps to classes and templates).
   
-  First, we'll update the Start.tml template, to create a link for creating a new page:
+  First, we'll update the Index.tml template, to create a link for creating a new page:
   
-  <<src/main/webapp/Start.tml:>>
+  <<src/main/webapp/Index.tml:>>
 
 ----
     <h1>Address Book</h1>
@@ -208,7 +208,7 @@
    
    So ... why is the class named "CreateAddress" and not simply "Create"?  Actually, we could have named it "Create", and 
    the application would still work, but the longer <class> name is equally valid.  Tapestry noticed the redundancy in the
-   class name:  <<<org.apache.tapestry.tutorial1.pages.>>><address><<<.Create>>><Address> and just stripped it out.
+   class name:  <<<org.apache.tapestry.tutorial.pages.>>><address><<<.Create>>><Address> and just stripped it out.
    
    Eventually, your application will probably have more entities:  perhaps you'll have a "user/Create" page and a "payment/Create" page and an "account/Create" page.
    Now, you <could> have a bunch of different classes named <<<Create>>> spread across a number of different packages.  That's legal Java, but it isn't ideal.  You may find yourself
@@ -216,6 +216,13 @@
    
    Tapestry is encouraging you to use a more descriptive name: <<<Create>>><Address> not just <<<Create>>>, but it isn't making you pay the cost (in terms of longer,
    uglier URLs).  The URL to access the page will still be http://localhost:8080/tutorial1/address/create.
+
+   Another note:  Index pages work in folders as well.  A class named org.apache.tapestry.tutorial.pages.address.AddressIndex would be given the name
+   "address/Index".  However, Tapestry has special rules for pages named "Index" and the render URL would be
+   <<<http://localhost:8080/tutorial1/address"/>>>.  In other words, you can place Index pages in any folder and Tapestry will
+   build a short URL for that page ... and you <don't> have to keep naming the classes Index (it's confusing to have many classes with the same
+   name, even across multiple packages); instead, you can name each index page after the package
+   that contains it.  Tapestry users a smart <convention> to keep it all straight and generate short, to the point URLs.
    
 * Using the BeanEditForm component
 
@@ -417,6 +424,8 @@
   Restart the application, and refresh your browser, then hit the submit button.
     
 [address-v6.png] Form with client side validations visible
+
+  <<TODO: Update this with a more recent image showing the new pop-up bubbles style of displaying client-side error messages.>>
 
   What this picture doesn't show is that the validation occurred entirely <on the client side>, using JavaScript. Look what's happened: a summary of <all> the
   errors in the form has scrolled down at the top of the form, and each form in error has been highlighted (it's a bit subtle) and marked with a red "X".  Further, the label

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/hilo.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/hilo.apt?rev=631068&r1=631067&r2=631068&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/hilo.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/hilo.apt Mon Feb 25 18:15:43 2008
@@ -18,9 +18,9 @@
   a link to start guessing.  The Guess page presents the user with ten links, plus feedback such as "too low" or "too high".  
   The GameOver page tells the user how many guesses they took.
   
-  Let's get to work on the Start page and template.
+  Let's get to work on the Index page and template.
   
-  <<src/main/webapp/Start.tml:>>
+  <<src/main/webapp/Index.tml:>>
   
 ----
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
@@ -79,28 +79,33 @@
 {
   private int _target;
 
-  void setup(int target)
+  Object initialize(int target)
   {
     _target = target;
+
+    return this;
   }
 }
 ----
 
-  The key method here is setup():  It is invoked to tell the Guess page what the target number is.  Notice that is is package private, not public; 
-  it is only expected to be invoked from the Start page (as we'll see in a moment), so there's no need to make it public.  Later we'll see that
-  there's more setup than just initializing the _target instance variable (which is why we don't name the method setTarget() ).
+  The key method here is initialize():  It is invoked to tell the Guess page what the target number is.  Notice that the
+   method is package private, not public;
+  it is only expected to be invoked from the Index page (as we'll see in a moment), so there's no need to make it public.  Later we'll see that
+  there's more initialization to be done
+  than just storing a value into the _target instance variable (which is why we don't simply name the method setTarget() ).
   
   <The naming convention, using leading underscores for fields, is NOT a requirement of Tapestry; that's
    just my personal coding style.>
 
-  Now we can move back to the Start page.  What we want is to have the ActionLink component invoke a method on our page. We can then generate
-  a random target number. We'll tell the Guess page what the target number is and then make sure is the the Guess page, and not the Start page,
+  Now we can move back to the Index page.  What we want is to have the ActionLink component invoke a method on the Index page. We can then generate
+  a random target number. We'll tell the Guess page what the target number is and then make sure that it is  the Guess page, and not the
+   Index page,
   that renders the response into the user's web browser.  That's actually quite a few concepts to take in all at once.
   
   Let's start with the code, and break it down:
   
-  <<src/main/java/org/apache/tapestry/tutorial/pages/Start.java>>
-  
+  <<src/main/java/org/apache/tapestry/tutorial/pages/Index.java>>
+
 ----
 package org.apache.tapestry.tutorial.pages;
 
@@ -119,23 +124,27 @@
   {
     int target = _random.nextInt(10) + 1;
 
-    _guess.setup(target);
-
-    return _guess;
+    return _guess.initialize(target);
   }
 }
 ----
   
-  What we're talking about here is <communication> of information from the Start page to the Guess page.  In traditional servlet development, this is done
+  What we're talking about here is <communication> of information from the Index page to the Guess page.  In traditional servlet development, this is done
   in a bizarre way ... storing attributes into the shared HttpSession object.  Of course, for that to work, both (or all) parties have to agree on the type
   of object stored, and the well-known name used to access the attribute.  That's the source of a large number of bugs.  It's also not very object oriented ... state
   is something that should be <inside> objects (and private), not <outside> objects (and public).
   
   The Tapestry way is very object oriented: everything is done in terms of objects and methods and properties of those objects.
   
-  This communication starts with the connection between the two pages:  in this case, the InjectPage annotation allows another page in the application
-  to be injected into the Start page.  
-  
+  This communication starts with the connection between the two pages:  in this case, the
+  {{{../apidocs/org/apache/tapestry/annotations/InjectPage.html}InjectPage}} annotation allows another page in the application
+  to be injected into the Index page.
+
+  Injection can be a somewhat nebulous concept.  In terms of Tapestry, it means that some cooperating object needed by one class is provided to it.  The other
+  object is often referred to as a "dependency"; in this case, the Index page <depends on> the Guess page to be fully functional, and an instance of the Guess
+  page is made available as the _guess instance variable.  The Index page doesn't, and can't, <create> the Guess page, it can only
+  advertise, to Tapestry, that it needs the Guess page.  Tapestry will take care of the rest.
+    
   Let's see what we do with this injected page.  It's used inside onAction().  You might guess that this method is invoked when the link ("Start guessing") is clicked.  But
   why?
   
@@ -143,7 +152,7 @@
   is "action" and the component id is not even specified. This translates to "when the action event is fired from any component, invoke this method".
   
   "The action event?"  This underlines a bit about how Tapestry processes requests. When you click a link generated by the ActionLink component, Tapestry is able
-  to identify the underlying component inside the request: it knows that the component is on the Start page, and it knows the component within the page.  Here we didn't give
+  to identify the underlying component inside the request: it knows that the component is on the Index page, and it knows the component within the page.  Here we didn't give
   the ActionLink component a specific id, so Tapestry supplied one.  An "action" event is triggered inside the ActionLink component, and that event bubbles
   up to the page, where the onAction() method acts as an <event handler method>.
   
@@ -156,14 +165,17 @@
   What can you do inside an event handler method?  Any kind of business logic you like; Tapestry doesn't care.  Here we're using a random number generator
   to set the target number to guess.
   
-  We also use the injected Guess page; we invoke the setup() method to tell it about the number the user is trying to guess.
+  We also use the injected Guess page; we invoke the initialize() method to tell it about the number the user is trying to guess.
   
   The <return value> of an event handler method is very important; the value returned informs Tapestry about what page will render the response to the client.
   By returning the injected Guess page, we're telling Tapestry that the Guess page should be the one to render the response.
+
+  This idiom: having the Guess page provide an initialize() method and return itself, is very common in Tapestry. It allows the event handler method to be
+  very succinct; it's as if the event handler method says "initialize the Guess page and render it to the client".
   
   Again, this is a big difference between Tapestry and servlets (or Struts). Tapestry tightly binds the controller (the Java class) to the template.
-  Using JSPs, you would have extra configuration to select a view (usally by a logic name, such as "success") to a "view" (a JSP).  Tapestry cuts through
-  all that cruft for you.
+  Using JSPs, you would have extra configuration to select a view (usually by a logical name, such as "success") to a "view" (a JSP).  Tapestry cuts through
+  all that cruft for you. Objects communicate with, and defer to, other objects and all the templates and rendering machinery comes along for free.
   
   In later chapters, we'll see other possibilities besides returning a page instance from an event handler method.
   
@@ -175,7 +187,7 @@
   
   Something was wrong with the Guess page, and Tapestry has reported the error to you so that you can make a correction.
   
-  Here, the root problem was that we didn't define a getTarget() method in the Start class.  Ooops.  Deep inside Tapestry, a RuntmeException was thrown to explain this.
+  Here, the root problem was that we didn't define a getTarget() method in the Guess class.  Ooops.  Deep inside Tapestry, a RuntmeException was thrown to explain this.
   
   As often happens in frameworks, that RuntimeException was caught and rethrown wrapped inside a new exception, the TapestryException. This added a bit more detail to the exception
   message, and linked the exception to a <location>.  Since the error occurred inside a component template, Tapestry is able to display that portion of the
@@ -240,7 +252,7 @@
   at the end of that request, the value was lost, and in the subsequent render request for the Guess page,
   the value was zero.
   
-  Fortunately, it is very easy to transcend this behavior.  We'll use an annotation, @Persist,
+  Fortunately, it is very easy to transcend this behavior.  We'll use an annotation, {{{../apidocs/org/apache/tapestry/annotations/Persist.html}Persist}},
   on the instance variable:
   
 ----
@@ -252,7 +264,7 @@
  
 [hilo-number.png] The target number
   
-  One of the nice things about this approach, the user of redirects, is that hitting the refresh button
+  One of the nice things about this approach, the use of redirects, is that hitting the refresh button
   does <not> choose a new target number.  It simply redraws the Guess page with the 
   target number previously selected. In many servlet applications, the URL would be for the action "choose a random number"
   and refreshing would re-execute that action.
@@ -279,7 +291,7 @@
 
   <p>Make a guess between one and ten:</p>
   
-    <t:loop source="1..10" value="guess">
+    <t:loop source="1..10" value="guess" xml:space="preserve">
       <t:actionlink t:id="link" context="guess">${guess}</t:actionlink>
     </t:loop>
 
@@ -290,7 +302,14 @@
   The Loop component's source attribute identifies the values to loop over.
   Often this is a list or array, but here the special
   special syntax, "1..10" means iterate over the numbers between 1 and 10, inclusive.
-  
+
+  What about the <<<xml:space="preserve">>> attribute?  Normally, Tapestry is pretty draconian
+  about stripping out unnecessary whitespace from the template.  Most whitespace (spaces, tabs,
+  newlines, etc.) is reduced to a single space.  This can help a lot with reducing the size
+  of the output and with making complex nested layouts easier to read ... but occasionally, as here,
+  the whitespace is needed to keep the numbers from running together.  <<<xml:space="preserve">>>
+  turns on full whitespace retention for the element.
+
   The value attribute gets assigned the current item from the loop.  We'll use
   a property of the Guess page as a kind of scratchpad for this purpose:
   
@@ -314,7 +333,9 @@
   request URL.  The end result is <<<http://localhost:8080/tutorial1/guess.link/4>>>.  
   
   What is "guess.link"?  That's the name of the page, "guess", and the id of the component 
-  ("link", as explicitly set with the t:id attribute).
+  ("link", as explicitly set with the t:id attribute).        Remember this is an action link:
+  as soon as the user clicks the click, it is replaced with a render link such
+  as <<<http://localhost:8080/tutorial1/guess>>>.
   
   Now, to handle those guesses. We're going to add an event handler method that gets
   invoked when a link is clicked.  We're also going to add a new property, message, to
@@ -343,11 +364,14 @@
 ---  
 
 
-  
-  
   Here's the big news: Tapestry will convert the number from the URL back into
-  an integer automatically, so that it can pass it in to this method as a parameter.
+  an integer automatically, so that it can pass it in to the onActionFromLink() event handler method as a
+  method parameter.
   We can then compare the guess from the user to the secret target number.
+
+  Notice how Tapestry adapts to the return value.  Here is may be null ... Tapestry interprets that
+  as "stay on the same page".  You may also return a string ("GameOver"); Tapestry interprets <that>
+  as the name of the page to render the response.
   
 
   We need to update the Guess page to actually display the message; this is done by adding the following:
@@ -411,10 +435,12 @@
   precaution in case we add logic to play the game again.
   
 ---
-  void setup(int target)
+  Object initialize(int target)
   {
     _target = target;
     _count = 0;
+
+    return this;
   }
 --- 
 
@@ -427,11 +453,7 @@
   {
     _count++;
 
-    if (guess == _target)
-    {
-      _gameOver.setup(_count);
-      return _gameOver;
-    }
+    if (guess == _target) return _gameOver.initialize(_count);
 
     if (guess < _target)
       _message = String.format("%d is too low.", guess);
@@ -443,7 +465,7 @@
 ---
 
   So, we update the count before comparing and, instead of returning the
-  name over the GameOver page, we return the configured instance.
+  name of the GameOver page, we return the configured instance.
   
   Lastly, we need to make some changes to the GameOver class.
   
@@ -464,9 +486,11 @@
     return _count;
   }
 
-  void setup(int count)
+  Object initialize(int count)
   {
     _count = count;
+
+    return this;
   }
 }
 ---