You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/01/28 23:43:53 UTC

svn commit: r500908 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/internal/structure/ main/java/org/apache/tapestry/internal/test/ main/ja...

Author: hlship
Date: Sun Jan 28 14:43:52 2007
New Revision: 500908

URL: http://svn.apache.org/viewvc?view=rev&rev=500908
Log:
Change page render URLs to consist of just the page name (and possibly, the page passivation context) with no extension.
Make component ids accessible in a case-insensitive manner.
Generate URLs are all-lower case (for the page name and component id portion).

Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandler.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/AssetDemo.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Barney.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Fred.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Wilma.html

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -34,7 +34,8 @@
 
     /**
      * Return a string consisting the concatinated ids of all containing components, separated by
-     * periods. I.e., "foo.bar.baz". Returns null for a page.
+     * periods. In addition, nested ids are always all lower case. I.e., "foo.bar.baz". Returns null
+     * for a page.
      */
 
     String getNestedId();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -37,8 +37,9 @@
     {
         StringBuilder builder = new StringBuilder();
 
-        builder.append(_pageName);
+        builder.append(_pageName.toLowerCase());
         builder.append(".");
+        // Already lower case by design.
         builder.append(_componentNestedId);
         builder.append(".");
         builder.append(_action);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -173,6 +173,11 @@
             throw new IllegalArgumentException(ServicesMessages.couldNotResolvePageName(pageName));
 
         return result;
+    }
+
+    public boolean isPageName(String pageName)
+    {
+        return locate(pageName, _pageToClassName) != null;
     }
 
     public String resolveComponentTypeToClassName(String componentType)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandler.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandler.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandler.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,9 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-public interface PageLinkHandler
-{
-    void handle(ComponentInvocation invocation, PageRenderer renderer);
-}
+package org.apache.tapestry.internal.services;
+
+/**
+ * Handles a invocation related to rendering out a pages complete content.
+ * <p>
+ * TODO: This should be called RenderLinkHandler.
+ */
+public interface PageLinkHandler
+{
+    void handle(String logicalPageName, Object[] context, PageRenderer renderer);
+
+    void handle(ComponentInvocation invocation, PageRenderer renderer);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkHandlerImpl.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -29,6 +29,14 @@
     public PageLinkHandlerImpl(RequestPageCache cache)
     {
         _cache = cache;
+    }
+
+    public void handle(String logicalPageName, Object[] context, PageRenderer renderer)
+    {
+        PageLinkTarget target = new PageLinkTarget(logicalPageName);
+        ComponentInvocation invocation = new ComponentInvocation(target, context);
+
+        handle(invocation, renderer);
     }
 
     public void handle(ComponentInvocation invocation, PageRenderer renderer)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLinkTarget.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,8 +14,6 @@
 
 package org.apache.tapestry.internal.services;
 
-import org.apache.tapestry.internal.InternalConstants;
-
 /**
  * It represents a component invocation target for a page link. It is passed to an
  * {@link org.apache.tapestry.internal.services.ActionLinkHandler} by both the
@@ -36,7 +34,7 @@
 
     public String getPath()
     {
-        return _pageName + "." + InternalConstants.TEMPLATE_EXTENSION;
+        return _pageName.toLowerCase();
     }
 
     public String getPageName()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,26 +16,30 @@
 
 import java.io.IOException;
 
-import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.services.ComponentClassResolver;
 import org.apache.tapestry.services.Dispatcher;
 import org.apache.tapestry.services.Request;
 import org.apache.tapestry.services.Response;
 
 /**
- * Dispatches incoming requests whose path ends with ".html". In these cases, the path is
- * interpreted as a page name.
+ * Dispatches incoming requests for render requests. Render requests consist of either just a
+ * logical page name (case insensitive) or a logical page name plus additional context. Because of
+ * this structure, it take a little bit of work to identify the split point between the page name
+ * and the context.
  */
 public class PageRenderDispatcher implements Dispatcher
 {
-    private final PageLinkHandler _handler;
+    private final ComponentClassResolver _componentClassResolver;
 
-    private PageResponseRenderer _renderer;
+    private final PageLinkHandler _handler;
 
-    private final String _suffix = "." + InternalConstants.TEMPLATE_EXTENSION;
+    private final PageResponseRenderer _renderer;
 
-    public PageRenderDispatcher(PageLinkHandler handler, PageResponseRenderer renderer)
+    public PageRenderDispatcher(ComponentClassResolver componentClassResolver,
+            PageLinkHandler handler, PageResponseRenderer renderer)
     {
+        _componentClassResolver = componentClassResolver;
         _handler = handler;
         _renderer = renderer;
 
@@ -43,51 +47,55 @@
 
     public boolean dispatch(Request request, final Response response) throws IOException
     {
+        // Rememeber that the path starts with a leading slash that is not part of the logical page
+        // name.
+
         String path = request.getPath();
 
-        // For the moment, just matching things that end with .html
+        int searchStart = 1;
 
-        int pos = path.indexOf(_suffix);
+        while (true)
+        {
+            int nextslashx = path.indexOf('/', searchStart);
 
-        if (pos < 0)
-            return false;
+            boolean atEnd = nextslashx < 0;
 
-        // We have a match!
+            String pageName = atEnd ? path.substring(1) : path.substring(1, nextslashx);
 
-        // The first character will be the leading slash
+            if (_componentClassResolver.isPageName(pageName))
+            {
+                // TODO: UUDecode the strings in the context?
 
-        String logicalPageName = path.substring(1, pos);
+                Object[] context = atEnd ? new Object[0] : path.substring(nextslashx + 1)
+                        .split("/");
 
-        String[] terms = path.substring(pos).split("/");
+                PageRenderer renderer = new PageRenderer()
+                {
+                    public void renderPage(Page page)
+                    {
+                        try
+                        {
+                            _renderer.renderPageResponse(page, response);
+                        }
+                        catch (IOException ex)
+                        {
+                            new RuntimeException(ex);
+                        }
 
-        Object[] context = new Object[terms.length - 1];
+                    }
+                };
 
-        for (int i = 1; i < terms.length; i++)
-        {
-            // TODO: Decode strings?
-            context[i - 1] = terms[i];
-        }
-
-        PageRenderer renderer = new PageRenderer()
-        {
-            public void renderPage(Page page)
-            {
-                try
-                {
-                    _renderer.renderPageResponse(page, response);
-                }
-                catch (IOException ex)
-                {
-                    new RuntimeException(ex);
-                }
+                _handler.handle(pageName, context, renderer);
 
+                return true;
             }
-        };
 
-        _handler.handle(
-                new ComponentInvocation(new PageLinkTarget(logicalPageName), context),
-                renderer);
+            if (atEnd)
+                return false;
+
+            // Advance to the next slash within the path.
 
-        return true;
+            searchStart = nextslashx + 1;
+        }
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -68,7 +68,7 @@
 
     /**
      * Adds a component to its container. The embedded component's id must be unique within the
-     * container.
+     * container (after the id is converted to lower case).
      */
     void addEmbeddedElement(ComponentPageElement child);
 
@@ -81,7 +81,7 @@
     void addMixin(Instantiator instantiator);
 
     /**
-     * Retrieves a component page element by its id.
+     * Retrieves a component page element by its id. The search is caseless.
      * 
      * @param id
      *            used to locate the element
@@ -118,8 +118,7 @@
     /**
      * Posts a change to a persistent field. If the component is still loading, then this change is
      * ignored. Otherwise, it is propogated, via the
-     * {@link Page#persistFieldChange(ComponentResources, String, Object) page}
-     * to the
+     * {@link Page#persistFieldChange(ComponentResources, String, Object) page} to the
      * {@link PersistentFieldManager#postChange(String, ComponentResources, String, Object) PersistentFieldManager}.
      */
     void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -546,17 +546,19 @@
 
         _coreComponent = _coreResources.getComponent();
 
-        String componentClassName = _coreComponent.getClass().getName();
+        String pageName = _page.getName();
 
         // A page (really, the root component of a page) does not have a container.
 
         if (container == null)
         {
-            _completeId = componentClassName;
+            _completeId = pageName;
             _nestedId = null;
         }
         else
         {
+            String caselessId = id.toLowerCase();
+
             String parentNestedId = container.getNestedId();
 
             // The root element has no nested id.
@@ -564,13 +566,13 @@
 
             if (parentNestedId == null)
             {
-                _nestedId = id;
-                _completeId = _page.getName() + ":" + id;
+                _nestedId = caselessId;
+                _completeId = _page.getName() + ":" + caselessId;
             }
             else
             {
-                _nestedId = parentNestedId + "." + id;
-                _completeId = container.getCompleteId() + "." + id;
+                _nestedId = parentNestedId + "." + caselessId;
+                _completeId = container.getCompleteId() + "." + caselessId;
             }
         }
     }
@@ -589,7 +591,12 @@
         if (_children == null)
             _children = newMap();
 
-        String childId = child.getId();
+        String childId = child.getId().toLowerCase();
+
+        ComponentPageElement existing = _children.get(childId);
+        if (existing != null)
+            throw new TapestryException(StructureMessages.duplicateChildComponent(this, childId),
+                    child, null);
 
         // TODO: Check for conflicts!
 
@@ -772,7 +779,8 @@
     /**
      * Delegates to the
      * {@link Page#createActionLink(Element, ComponentPageElement, String, boolean, Object[]) the containing page}.
-     * Why the extra layer? Trying to avoid some unwanted injection (of LinkFactory, into every component page element).
+     * Why the extra layer? Trying to avoid some unwanted injection (of LinkFactory, into every
+     * component page element).
      */
     public Link createActionLink(String action, boolean forForm, Object... context)
     {
@@ -824,7 +832,8 @@
 
     public ComponentPageElement getEmbeddedElement(String embeddedId)
     {
-        ComponentPageElement embeddedElement = InternalUtils.get(_children, embeddedId);
+        ComponentPageElement embeddedElement = InternalUtils.get(_children, embeddedId
+                .toLowerCase());
 
         if (embeddedElement == null)
             throw new TapestryException(StructureMessages.noSuchComponent(this, embeddedId), this,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -67,6 +67,10 @@
 
     public ComponentPageElement getComponentElementByNestedId(String nestedId)
     {
+        // TODO: Especially with the addition of all the caseless logic, and with respect to how
+        // forms are implemented, it may be worthwhile to cache the key to element mapping. I think
+        // we're going to do it a lot!
+
         ComponentPageElement element = _rootElement;
 
         for (String id : nestedId.split("\\."))
@@ -153,7 +157,6 @@
         return _linkFactory.createActionLink(element, action, forForm, context);
     }
 
-    
     public Link createPageLink(String pageName)
     {
         return _linkFactory.createPageLink(pageName);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -84,5 +84,10 @@
     static String pageIsDirty(Page page)
     {
         return MESSAGES.format("page-is-dirty", page);
+    }
+
+    static String duplicateChildComponent(ComponentPageElement container, String childId)
+    {
+        return MESSAGES.format("duplicate-child-component", container.getCompleteId(), childId);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright , 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
 import org.apache.tapestry.internal.services.PageElementFactory;
 import org.apache.tapestry.internal.services.PageLoader;
 import org.apache.tapestry.internal.services.PagePool;
+import org.apache.tapestry.internal.services.PageResponseRenderer;
 import org.apache.tapestry.internal.services.PageTemplateLocator;
 import org.apache.tapestry.internal.services.RequestPageCache;
 import org.apache.tapestry.internal.services.ResourceCache;
@@ -164,7 +165,7 @@
 
     protected final void train_getName(Page page, String name)
     {
-        expect(page.getName()).andReturn(name);
+        expect(page.getName()).andReturn(name).atLeastOnce();
     }
 
     protected final PagePool newPagePool()
@@ -195,7 +196,7 @@
 
     protected final void train_getComponent(ComponentPageElement element, Component component)
     {
-        expect(element.getComponent()).andReturn(component);
+        expect(element.getComponent()).andReturn(component).atLeastOnce();
     }
 
     protected final void train_getId(ComponentResourcesCommon resources, String id)
@@ -473,5 +474,20 @@
     protected final void train_isLoaded(InternalComponentResources resources, boolean isLoaded)
     {
         expect(resources.isLoaded()).andReturn(isLoaded);
+    }
+
+    protected final void stub_isPageName(ComponentClassResolver resolver, boolean result)
+    {
+        expect(resolver.isPageName(isA(String.class))).andStubReturn(result);
+    }
+
+    protected final void train_isPageName(ComponentClassResolver resolver, String pageName, boolean result)
+    {
+        expect(resolver.isPageName(pageName)).andReturn(result);
+    }
+
+    protected final PageResponseRenderer newPageResponseRenderer()
+    {
+        return newMock(PageResponseRenderer.class);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,102 +12,94 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
-
-/**
- * Resolves page names and component types to fully qualified class names. Pages and components may
- * be provided by the application or inside a <em>mapped package</em>. Page names often appear
- * inside URLs, and component types often appear in component template (when specifying the type of
- * an embedded component).
- * <p>
- * The service is configured using a collection of {@link LibraryMapping}s. Each mapping maps a
- * prefix, such as "core" to a root package name, such as "org.apache.tapestry.corelib". The root
- * package is expected to have two sub-packages, "pages" and "components". Page names are searched
- * for in the pages package, and components types are searched for the components package.
- * <p>
- * When searching, the portion of the logical page name (or component type) before the slash is
- * compared with known prefixes in order to find the correct root package name. Thus when
- * encountering the page name (or component type) "foo/Bar", a search for a mapping for prefix "foo"
- * will occur. If "foo" is mapped, then the final class name will be built from foo's root package,
- * then "pages" (or "components"), then "Bar".
- * <p>
- * If "foo" is not mapped, then the name is resolved as part of the application. The final class
- * name will be "<em>application-root</em>.pages.foo.Bar", meaning that the prefix is
- * interpreted as a package name under the appropriate root package.
- * <p>
- * Prefixes are only search for based on using the forward slash ("/") as a delimiter. Following the
- * slash, a period (".") may be used to signify, unambiguously, a sub-package.
- * <p>
- * Prefixes themselves may be more complex; a search for "foo/bar/Baz" will first search for a
- * "foo/bar" prefix, then (if not found), a "foo" prefix.
- * <p>
- * When a package name or component type can not be resolved as is, the special library mapping,
- * "core" is used (this represents the Tapestry core components, as package under
- * org.apache.tapestry.corelib). In this way, the names of "builtin" components can be referenced as
- * if they were part of the application. Further, an application may <em>override</em> the builtin
- * components (though this should rarely, if ever, be necessary and should only be undertaken with
- * extreme caution).
- * <p>
- * A given mapping may have <em>multiple root packages</em>. This is occasionally useful, but
- * (again) should be used with care, as this can cause distruptive behavior in the application if
- * abused, especiallyl in the face of ambiguities such as duplicate named classes in different
- * packages mapped to the same prefix.
- * <p>
- * Certain ambiguities occur if mapped packages overlap, either in terms of the the prefixes or the
- * package names. Keep things clearly seperate to avoid lookup problems.
- */
-public interface ComponentClassResolver
-{
-    /**
-     * Converts a logical page name (such as might be encoded into a URL) into a fully qualified
-     * class name.
-     * 
-     * @param pageName
-     *            partial name
-     * @return fully qualified class name of null if the page name can not be resolved
-     */
-    String resolvePageNameToClassName(String pageName);
-
-    /**
-     * Converts a fully qualified page class name into a logical class name (often, for inclusion in
-     * as part of the URI.
-     * 
-     * @param pageClassName
-     *            fully qualified name of a page class
-     * @return equivalent logical page name
-     * @throws IllegalArgumentException
-     *             if the name can not be resolved
-     */
-    String resolvePageClassNameToPageName(String pageClassName);
-
-    /**
-     * Converts a component type (a logical component name such as might be used inside a template
-     * or annotation) into a fully qualified class name.
-     * 
-     * @param componentType
-     *            a logical component type
-     * @return fully qualified class name
-     * @throws IllegalArgumentException
-     *             if the component type can not be resolved
-     */
-    String resolveComponentTypeToClassName(String componentType);
-
-    /**
-     * Converts a logical mixin type (as with component types) into a fully qualified class name.
-     * 
-     * @param mixinType
-     *            a logical mixin type
-     * @return fully qualified class name
-     * @throws IllegalArgumentException
-     *             if the mixin type can not be resolved
-     */
-    String resolveMixinTypeToClassName(String mixinType);
-
-    /**
-     * Sets the root package for the application. Pages are expected to be in a sub-package named
-     * "pages" (i.e., root.pages). Components will be in a sub-package named "components".
-     * 
-     * @param packageName
-     */
-    void setApplicationPackage(String packageName);
-}
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.internal.services.ComponentClassLocator;
+
+/**
+ * Resolves page names and component types to fully qualified class names. Pages and components may
+ * be provided by the application or inside a <em>mapped package</em>. Page names often appear
+ * inside URLs, and component types often appear in component template (when specifying the type of
+ * an embedded component).
+ * <p>
+ * The service is configured using a collection of {@link LibraryMapping}s. Each mapping maps a
+ * prefix, such as "core" to a root package name, such as "org.apache.tapestry.corelib". The root
+ * package is expected to have sub-packages: "pages", "components", "mixins" and "base" ("base" is
+ * for base classes).
+ * <p>
+ * The resolver performs a search of the classpath (via {@link ComponentClassLocator}, to build up
+ * a set of case-insensitive maps from logical page name, component type, or mixin type to fully
+ * qualified class name.
+ * <p>
+ * Certain ambiguities occur if mapped packages overlap, either in terms of the the prefixes or the
+ * package names. Keep things clearly seperate to avoid lookup problems.
+ */
+public interface ComponentClassResolver
+{
+    /**
+     * Converts a logical page name (such as might be encoded into a URL) into a fully qualified
+     * class name. The case of the page name is irrelevant.
+     * 
+     * @param pageName
+     *            logical page name
+     * @return fully qualified class name for the page
+     * @throws IllegalArgumentException
+     *             if the name does not match a known page class
+     */
+    String resolvePageNameToClassName(String pageName);
+
+    /**
+     * For a particular path, determines if the path is a logical page name. The check is case
+     * insensitive.
+     * 
+     * @param pageName
+     *            potential logical page name
+     * @return true if the page name is valid
+     */
+    boolean isPageName(String pageName);
+
+    /**
+     * Converts a fully qualified page class name into a logical class name (often, for inclusion as
+     * part of the URI). This value may later be passed to
+     * {@link #resolvePageNameToClassName(String)}.
+     * 
+     * @param pageClassName
+     *            fully qualified name of a page class
+     * @return equivalent logical page name
+     * @throws IllegalArgumentException
+     *             if the name can not be resolved
+     */
+    String resolvePageClassNameToPageName(String pageClassName);
+
+    /**
+     * Converts a component type (a logical component name such as might be used inside a template
+     * or annotation) into a fully qualified class name. Case is ignored in resolving the name.
+     * 
+     * @param componentType
+     *            a logical component type
+     * @return fully qualified class name
+     * @throws IllegalArgumentException
+     *             if the component type can not be resolved
+     */
+    String resolveComponentTypeToClassName(String componentType);
+
+    /**
+     * Converts a logical mixin type (as with component types) into a fully qualified class name.
+     * Case is ignored when resolving the name.
+     * 
+     * @param mixinType
+     *            a logical mixin type
+     * @return fully qualified class name
+     * @throws IllegalArgumentException
+     *             if the mixin type can not be resolved
+     */
+    String resolveMixinTypeToClassName(String mixinType);
+
+    /**
+     * Sets the root package for the application. Pages are expected to be in a sub-package named
+     * "pages" (i.e., root.pages). Components will be in a sub-package named "components".
+     * 
+     * @param packageName
+     */
+    void setApplicationPackage(String packageName);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -512,7 +512,9 @@
             ResourceCache resourceCache, @InjectService("tapestry.internal.ResourceStreamer")
             ResourceStreamer streamer, @InjectService("PageLinkHandler")
             PageLinkHandler pageLinkHandler, @InjectService("ActionLinkHandler")
-            ActionLinkHandler actionLinkHandler)
+            ActionLinkHandler actionLinkHandler,
+            @InjectService("tapestry.ComponentClassResolver")
+            ComponentClassResolver componentClassResolver)
     {
         // This goes first because an asset to be streamed may have an file extension, such as
         // ".html", that will confuse the later dispatchers.
@@ -522,10 +524,8 @@
                 new AssetDispatcher(streamer, aliasManager, resourceCache),
                 "before:PageRender");
 
-        configuration.add("PageRender", new PageRenderDispatcher(pageLinkHandler,
-                _pageResponseRenderer));
-
-        // This goes after the HTML one so that the "." in ".html" doesn't confuse it.
+        configuration.add("PageRender", new PageRenderDispatcher(componentClassResolver,
+                pageLinkHandler, _pageResponseRenderer));
 
         configuration.add(
                 "ComponentEvent",
@@ -533,8 +533,7 @@
                 "after:PageRender");
     }
 
-    public PageLinkHandler buildPageLinkHandler(@InjectService("Response")
-    final Response response)
+    public PageLinkHandler buildPageLinkHandler()
     {
         return new PageLinkHandlerImpl(_requestPageCache);
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-# Copyright 2006 The Apache Software Foundation
+# Copyright 2006, 2007 The Apache Software Foundation
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -29,3 +29,5 @@
   it has invoked MarkupWriter.end() without first invoking MarkupWriter.element().
 page-is-dirty=Page %s was stored into the page pool in a dirty state. This should never happen, \
   and may indicate that a reference to the page (or component within the page) was retained past the end of a request.
+duplicate-child-component=Component %s already contains a child component with id '%s'. \
+  Embedded component ids must be unique (excluding case, which is ignored).
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt Sun Jan 28 14:43:52 2007
@@ -38,6 +38,10 @@
   Progress on Tapestry 5 is really taking off. This space lists some cool new features that have been added
   recently.
   
+  * Case insensitve URLs.  Tapestry no longer cares about the case of the page names and commponent ids it puts
+    into URLs, and they are now generated in all lower-case for that clean "Web 2.0" loop.  And they're 
+    shorter, too!
+  
   * Initial support for {{{guide/appstate.html}application state objects}}.
   
   * Input validation messages may now be overriden by providing a particular message key in the containing component's message catalog.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html Sun Jan 28 14:43:52 2007
@@ -13,85 +13,85 @@
                 <td>
                     <ul>
                         <li>
-                            <a href="Start.html">Start Page</a>
+                            <a href="Start">Start Page</a>
                         </li>
                         <li>
-                            <a href="MerryChristmas.html">Count Page</a>
+                            <a href="MerryChristmas">Count Page</a>
                         </li>
                         <li>
-                            <a href="InjectDemo.html">Inject Demo</a>
+                            <a href="InjectDemo">Inject Demo</a>
                         </li>
                         <li>
-                            <a href="Countdown.html">Countdown Page</a>
+                            <a href="Countdown">Countdown Page</a>
                         </li>
                         <li>
-                            <a href="ParameterConflict.html">Template Overriden by Class Page</a>
+                            <a href="ParameterConflict">Template Overriden by Class Page</a>
                         </li>
                         <li>
-                            <a href="EnvironmentalDemo.html">Environmental Annotation Useage</a>
+                            <a href="EnvironmentalDemo">Environmental Annotation Useage</a>
                         </li>
                         <li>
-                            <a href="Expansion.html">Expansion Page</a>
+                            <a href="Expansion">Expansion Page</a>
                         </li>
                         <li>
-                            <a href="MissingPage.html">Missing Page</a> -- Used to test exception
+                            <a href="MissingPage">Missing Page</a> -- Used to test exception
                             reporting </li>
                         <li>
-                            <a href="BadTemplate.html">BadTemplate Page</a> -- More exception
+                            <a href="BadTemplate">BadTemplate Page</a> -- More exception
                             reporting </li>
                         <li>
-                            <a href="ActionPage.html">Action Page</a> -- tests fixture for
+                            <a href="ActionPage">Action Page</a> -- tests fixture for
                             ActionLink component </li>
                         <li>
-                            <a href="InstanceMixin.html">InstanceMixin</a> -- Mixin added to
+                            <a href="InstanceMixin">InstanceMixin</a> -- Mixin added to
                             particular component instance </li>
                         <li>
-                            <a href="RenderPhaseOrder.html">RenderPhaseOrder</a> -- Order of
+                            <a href="RenderPhaseOrder">RenderPhaseOrder</a> -- Order of
                             operations when invoking render phase methods </li>
-                        <li><a href="SimpleForm.html">SimpleForm</a> -- first pass at writing Form
+                        <li><a href="SimpleForm">SimpleForm</a> -- first pass at writing Form
                             and TextField components </li>
                         <li>
-                            <a href="NumberSelect.html">NumberSelect</a> -- passivate/activate page
+                            <a href="NumberSelect">NumberSelect</a> -- passivate/activate page
                             context demo </li>                 
                     </ul>
                 </td>
                 <td>
                     <ul><li>
-                            <a href="Localization.html">Localization</a> -- accessing localized
+                            <a href="Localization">Localization</a> -- accessing localized
                             messages from the component catalog </li>
                         <li>
-                            <a href="AssetDemo.html">AssetDemo</a> -- declaring an using Assets </li>
+                            <a href="AssetDemo">AssetDemo</a> -- declaring an using Assets </li>
                         <li>
-                            <a href="ExpansionSubclass.html">ExpansionSubclass</a> -- components can
+                            <a href="ExpansionSubclass">ExpansionSubclass</a> -- components can
                             inherit templates from base classes </li>
                         <li>
-                            <a href="InjectComponentMismatch.html">InjectComponentMismatch</a> --
+                            <a href="InjectComponentMismatch">InjectComponentMismatch</a> --
                             check error reporting when @InjectComponent doesn't match the actual
                             field type </li>
                         <li>
-                            <a href="ParameterDefault.html">ParameterDefault</a> -- defaulter
+                            <a href="ParameterDefault">ParameterDefault</a> -- defaulter
                             methods for component parameters </li>
                         <li>
-                            <a href="ValidForm.html">ValidForm</a> -- server-side input validation</li>
+                            <a href="ValidForm">ValidForm</a> -- server-side input validation</li>
                         <li>
-                            <a href="AnyDemo.html">AnyDemo</a> -- test out the Any component </li>
+                            <a href="AnyDemo">AnyDemo</a> -- test out the Any component </li>
                         <li>
-                            <a href="PasswordFieldDemo.html">PasswordFieldDemo</a> -- test for the
+                            <a href="PasswordFieldDemo">PasswordFieldDemo</a> -- test for the
                             PasswordField component </li>
                         <li>
-                            <a href="RenderComponentDemo.html">RenderComponentDemo</a> -- components
+                            <a href="RenderComponentDemo">RenderComponentDemo</a> -- components
                             that "nominate" other components to render </li>
                         <li>
-                            <a href="BlockDemo.html">BlockDemo</a> -- use of blocks to control
+                            <a href="BlockDemo">BlockDemo</a> -- use of blocks to control
                             rendering </li>
                         <li>
-                            <a href="ToDoListVolatile.html">ToDo List (Volatile)</a> -- Loops and
+                            <a href="ToDoListVolatile">ToDo List (Volatile)</a> -- Loops and
                             Submit inside Form, volatile mode </li>
                         <li>
-                            <a href="ToDoList.html">ToDo List</a> -- Loops and Submit inside Form
+                            <a href="ToDoList">ToDo List</a> -- Loops and Submit inside Form
                             using a primary key encoder </li>
                         <li>
-                            <a href="FlashDemo.html">FlashDemo</a> -- demonstrate "flash" persistence
+                            <a href="FlashDemo">FlashDemo</a> -- demonstrate "flash" persistence
                         </li>
                     </ul>
                 </td>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -67,6 +67,60 @@
         ComponentClassResolver resolver = create(source, locator);
 
         assertEquals(resolver.resolvePageNameToClassName("SimplePage"), className);
+
+        verify();
+    }
+
+    @Test
+    public void is_page_name()
+    {
+        ComponentInstantiatorSource source = newComponentInstantiatorSource();
+        ComponentClassLocator locator = newComponentClassLocator();
+
+        train_for_app_packages(source);
+
+        String className = APP_ROOT_PACKAGE + ".pages.SimplePage";
+
+        train_locateComponentClassNames(locator, APP_ROOT_PACKAGE + ".pages", className);
+
+        replay();
+
+        ComponentClassResolver resolver = create(source, locator);
+
+        assertTrue(resolver.isPageName("SimplePage"));
+        assertTrue(resolver.isPageName("simplepage"));
+        assertFalse(resolver.isPageName("UnknownPage"));
+
+        verify();
+    }
+
+    @Test
+    public void is_page_name_for_core_page()
+    {
+        ComponentInstantiatorSource source = newComponentInstantiatorSource();
+        ComponentClassLocator locator = newComponentClassLocator();
+
+        train_for_app_packages(source);
+        train_for_packages(source, CORE_ROOT_PACKAGE);
+
+        String className = CORE_ROOT_PACKAGE + ".pages.CorePage";
+
+        train_locateComponentClassNames(locator, CORE_ROOT_PACKAGE + ".pages", className);
+
+        replay();
+
+        ComponentClassResolver resolver = create(source, locator, new LibraryMapping(CORE_PREFIX,
+                CORE_ROOT_PACKAGE));
+
+        // Can look like an application page, but still resolves to the core library class name.
+
+        assertTrue(resolver.isPageName("CorePage"));
+
+        // Or we can give it its true name
+
+        assertTrue(resolver.isPageName("core/corepage"));
+
+        assertFalse(resolver.isPageName("UnknownPage"));
 
         verify();
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@
                 PAGE_LOGICAL_NAME,
                 "foo.bar",
                 "someaction",
-                "/sub/MyPage.foo.bar.someaction");
+                "/sub/mypage.foo.bar.someaction");
     }
 
     @Test
@@ -59,7 +59,7 @@
                 PAGE_LOGICAL_NAME,
                 "foo.bar",
                 "publish",
-                "/sub/MyPage.foo.bar.publish/fred/5",
+                "/sub/mypage.foo.bar.publish/fred/5",
                 "fred",
                 5);
     }
@@ -73,7 +73,7 @@
                 PAGE_LOGICAL_NAME,
                 "foo.bar",
                 "someaction",
-                "/fred/sub/MyPage.foo.bar.someaction");
+                "/fred/sub/mypage.foo.bar.someaction");
     }
 
     @SuppressWarnings("unchecked")
@@ -133,7 +133,10 @@
                         (Object[]) EasyMock.isNull(),
                         EasyMock.isA(ComponentEventHandler.class))).andAnswer(triggerEventAnswer);
 
-        train_encodeRedirectURL(response, "/barney/" + PAGE_LOGICAL_NAME + ".html/foo/bar", ENCODED);
+        train_encodeRedirectURL(
+                response,
+                "/barney/" + PAGE_LOGICAL_NAME.toLowerCase() + "/foo/bar",
+                ENCODED);
 
         listener.createdPageLink(EasyMock.isA(Link.class));
         getMocksControl().andAnswer(createdPageLinkAnswer);
@@ -218,7 +221,10 @@
                         (Object[]) EasyMock.isNull(),
                         EasyMock.isA(ComponentEventHandler.class))).andAnswer(triggerEventAnswer);
 
-        train_encodeRedirectURL(response, "/barney/" + PAGE_LOGICAL_NAME + ".html/foo/bar", ENCODED);
+        train_encodeRedirectURL(
+                response,
+                "/barney/" + PAGE_LOGICAL_NAME.toLowerCase() + "/foo/bar",
+                ENCODED);
 
         listener.createdPageLink(EasyMock.isA(Link.class));
         getMocksControl().andAnswer(createdPageLinkAnswer);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,15 +14,19 @@
 
 package org.apache.tapestry.internal.services;
 
+import static org.easymock.EasyMock.aryEq;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.same;
+
 import org.apache.tapestry.ComponentEventHandler;
 import org.apache.tapestry.TapestryConstants;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.services.ComponentClassResolver;
 import org.apache.tapestry.services.Dispatcher;
 import org.apache.tapestry.services.Request;
 import org.apache.tapestry.services.Response;
-import org.easymock.EasyMock;
 import org.testng.annotations.Test;
 
 public class PageRenderDispatcherTest extends InternalBaseTestCase
@@ -30,17 +34,20 @@
     @Test
     public void not_a_page_request() throws Exception
     {
+        ComponentClassResolver resolver = newComponentClassResolver();
         PageResponseRenderer renderer = newPageResponseRenderer();
         RequestPageCache cache = newRequestPageCache();
         PageLinkHandler handler = new PageLinkHandlerImpl(cache);
         Request request = newRequest();
         Response response = newResponse();
 
+        stub_isPageName(resolver, false);
+
         train_getPath(request, "/foo/Bar.baz");
 
         replay();
 
-        Dispatcher d = new PageRenderDispatcher(handler, renderer);
+        Dispatcher d = new PageRenderDispatcher(resolver, handler, renderer);
 
         assertFalse(d.dispatch(request, response));
 
@@ -50,6 +57,7 @@
     @Test
     public void no_extra_context() throws Exception
     {
+        ComponentClassResolver resolver = newComponentClassResolver();
         PageResponseRenderer renderer = newPageResponseRenderer();
         RequestPageCache cache = newRequestPageCache();
         PageLinkHandler handler = new PageLinkHandlerImpl(cache);
@@ -58,7 +66,10 @@
         Page page = newPage();
         ComponentPageElement rootElement = newComponentPageElement();
 
-        train_getPath(request, "/foo/Bar.html");
+        train_getPath(request, "/foo/Bar");
+
+        train_isPageName(resolver, "foo", false);
+        train_isPageName(resolver, "foo/Bar", true);
 
         train_get(cache, "foo/Bar", page);
         train_getRootElement(page, rootElement);
@@ -74,7 +85,7 @@
 
         replay();
 
-        Dispatcher d = new PageRenderDispatcher(handler, renderer);
+        Dispatcher d = new PageRenderDispatcher(resolver, handler, renderer);
 
         assertTrue(d.dispatch(request, response));
 
@@ -84,6 +95,7 @@
     @Test
     public void context_passed_in_path() throws Exception
     {
+        ComponentClassResolver resolver = newComponentClassResolver();
         PageResponseRenderer renderer = newPageResponseRenderer();
         RequestPageCache cache = newRequestPageCache();
         PageLinkHandler handler = new PageLinkHandlerImpl(cache);
@@ -92,7 +104,10 @@
         Page page = newPage();
         ComponentPageElement rootElement = newComponentPageElement();
 
-        train_getPath(request, "/foo/Bar.html/zip/zoom");
+        train_getPath(request, "/foo/Bar/zip/zoom");
+
+        train_isPageName(resolver, "foo", false);
+        train_isPageName(resolver, "foo/Bar", true);
 
         train_get(cache, "foo/Bar", page);
         train_getRootElement(page, rootElement);
@@ -104,7 +119,7 @@
 
         replay();
 
-        Dispatcher d = new PageRenderDispatcher(handler, renderer);
+        Dispatcher d = new PageRenderDispatcher(resolver, handler, renderer);
 
         assertTrue(d.dispatch(request, response));
 
@@ -114,13 +129,7 @@
     private void train_triggerEvent(ComponentPageElement element, String eventType,
             Object[] context, ComponentEventHandler handler, boolean handled)
     {
-        expect(
-                element.triggerEvent(EasyMock.eq(eventType), EasyMock.aryEq(context), EasyMock
-                        .same(handler))).andReturn(handled);
-    }
-
-    protected final PageResponseRenderer newPageResponseRenderer()
-    {
-        return newMock(PageResponseRenderer.class);
+        expect(element.triggerEvent(eq(eventType), aryEq(context), same(handler))).andReturn(
+                handled);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java Sun Jan 28 14:43:52 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -32,10 +32,21 @@
 
 public class ComponentPageElementImplTest extends InternalBaseTestCase
 {
+    public static final String PAGE_NAME = "org.test.pages.Foo";
+
+    private Page newPage(String pageName)
+    {
+        Page page = newPage();
+
+        train_getName(page, pageName);
+
+        return page;
+    }
+
     @Test
     public void block_not_found()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -55,10 +66,8 @@
         }
         catch (BlockNotFoundException ex)
         {
-            assertTrue(ex
-                    .getMessage()
-                    .matches(
-                            "Template for component .* does not contain a block with identifier 'notFound'\\."));
+            assertTrue(ex.getMessage().contains(
+                    "does not contain a block with identifier 'notFound'."));
         }
 
         verify();
@@ -67,7 +76,7 @@
     @Test
     public void block_found()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -91,7 +100,7 @@
     @Test
     public void parameter_is_bound()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         Binding binding = newBinding();
@@ -121,7 +130,7 @@
     @Test
     public void verify_required_parameters_all_are_bound()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         Binding binding = newBinding();
@@ -149,7 +158,7 @@
     @Test
     public void verify_required_parameters_unbound_but_not_required()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         ParameterModel pmodel = newParameterModel();
@@ -230,7 +239,7 @@
     @Test
     public void is_invariant()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         Binding binding = newBinding();
@@ -260,7 +269,7 @@
     @Test
     public void read_binding()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -292,7 +301,7 @@
     @Test
     public void write_binding()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -324,7 +333,7 @@
     @Test
     public void get_embedded_does_not_exist()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -342,7 +351,7 @@
         }
         catch (TapestryException ex)
         {
-            assertEquals(ex.getMessage(), "Component " + component.getClass().getName()
+            assertEquals(ex.getMessage(), "Component " + PAGE_NAME
                     + " does not contain an embedded component with id 'unknown'.");
         }
 
@@ -352,7 +361,7 @@
     @Test
     public void get_existing_embedded_component()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         ComponentPageElement childElement = newComponentPageElement();
@@ -370,7 +379,46 @@
 
         cpe.addEmbeddedElement(childElement);
 
-        assertSame(childComponent, cpe.getComponentResources().getEmbeddedComponent("child"));
+        assertSame(cpe.getComponentResources().getEmbeddedComponent("child"), childComponent);
+
+        // Now check that the search is caseless.
+
+        assertSame(cpe.getComponentResources().getEmbeddedComponent("CHILD"), childComponent);
+
+        verify();
+    }
+
+    @Test
+    public void component_ids_must_be_unique_within_container()
+    {
+        Page page = newPage(PAGE_NAME);
+        Component pageComponent = newComponent();
+        ComponentModel model = newComponentModel();
+        ComponentPageElement child1 = newComponentPageElement();
+        ComponentPageElement child2 = newComponentPageElement();
+        TypeCoercer coercer = newTypeCoercer();
+
+        Instantiator ins = newInstantiator(pageComponent, model);
+
+        train_getId(child1, "Child");
+        train_getId(child2, "CHILD");
+
+        replay();
+
+        ComponentPageElement cpe = new ComponentPageElementImpl(page, ins, coercer, null);
+
+        cpe.addEmbeddedElement(child1);
+
+        try
+        {
+            cpe.addEmbeddedElement(child2);
+            unreachable();
+        }
+        catch (TapestryException ex)
+        {
+            assertTrue(ex.getMessage().contains(
+                    "already contains a child component with id 'child'."));
+        }
 
         verify();
     }
@@ -378,7 +426,7 @@
     @Test
     public void get_mixin_by_class_name()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -405,7 +453,7 @@
     @Test
     public void get_mixin_by_unknown_class_name()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         TypeCoercer coercer = newTypeCoercer();
@@ -440,7 +488,7 @@
     @Test
     public void set_explicit_parameter_of_unknown_mixin()
     {
-        Page page = newPage();
+        Page page = newPage(PAGE_NAME);
         Component component = newComponent();
         ComponentModel model = newComponentModel();
         ComponentModel mixinModel = newComponentModel();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/AssetDemo.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/AssetDemo.html?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/AssetDemo.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/AssetDemo.html Sun Jan 28 14:43:52 2007
@@ -15,8 +15,5 @@
     </p>
     
     <t:comp type="Img" src="button"/>
-    
-    <p>
-        [<a href="index.html">Back</a>]
-    </p>
+
 </t:comp>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Barney.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Barney.html?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Barney.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Barney.html Sun Jan 28 14:43:52 2007
@@ -9,6 +9,6 @@
     </p>
     
     <p>
-        [<a href="/InjectDemo.html">Back</a>]
+        [<a t:type="PageLink" t:page="InjectDemo">Back</a>]
     </p>
 </t:comp>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Fred.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Fred.html?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Fred.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Fred.html Sun Jan 28 14:43:52 2007
@@ -9,6 +9,6 @@
     </p>
     
     <p>
-[<a href="/InjectDemo.html">Back</a>]
+        [<a t:type="PageLink" t:page="InjectDemo">Back</a>]
     </p>
 </t:comp>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html Sun Jan 28 14:43:52 2007
@@ -4,7 +4,7 @@
     <p>  You chose ${selected}.</p>
     
     <p>
-    [<a href="/NumberSelect.html">Back</a>]
+        [<a t:type="PageLink" t:page="NumberSelect">Back</a>]
     </p>
     
 </t:comp>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Wilma.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Wilma.html?view=diff&rev=500908&r1=500907&r2=500908
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Wilma.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Wilma.html Sun Jan 28 14:43:52 2007
@@ -9,6 +9,6 @@
     </p>
     
     <p>
-        [<a href="/InjectDemo.html">Back</a>]
+        [<a t:type="PageLink" t:page="InjectDemo">Back</a>]
     </p>
 </t:comp>