You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2005/09/12 01:59:09 UTC

svn commit: r280219 - in /jakarta/tapestry/trunk: ./ framework/src/descriptor/META-INF/ framework/src/java/org/apache/tapestry/ framework/src/java/org/apache/tapestry/asset/ framework/src/java/org/apache/tapestry/binding/ framework/src/java/org/apache/...

Author: hlship
Date: Sun Sep 11 16:58:43 2005
New Revision: 280219

URL: http://svn.apache.org/viewcvs?rev=280219&view=rev
Log:
TAPESTRY-595: Resource prefixes not honored inside <page>'s specification-path attribute
TAPESTRY-578: Using component types with slashes in the HTML template fails

Added:
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/NamespaceResources.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/NamespaceResourcesImpl.java
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.html
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.jwc
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.library
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/services/impl/TestNamespaceResources.java
Modified:
    jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml
    jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.parse.xml
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/INamespace.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetFactory.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSource.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSourceImpl.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ContextAssetFactory.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/AbstractBinding.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingMessages.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingStrings.properties
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerMethodBinding.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/ISpecificationSource.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/Namespace.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/SpecificationParser.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/TemplateParser.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/SpecificationSourceImpl.java
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/BindingTestCase.java
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/TestListenerMethodBinding.java
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/TestComponentMessages.java
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestSpecificationParser.java
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestTemplateParser.java
    jakarta/tapestry/trunk/status.xml

Modified: jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml (original)
+++ jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml Sun Sep 11 16:58:43 2005
@@ -63,6 +63,7 @@
       <construct class="ContextAssetFactory">
         <set-service property="context" service-id="tapestry.globals.WebContext"/>
         <set-object property="contextPath" value="infrastructure:contextPath"/>
+        <set-object property="assetService" value="engine-service:asset"/>
       </construct>
     </invoke-factory>
     

Modified: jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.parse.xml
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.parse.xml?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.parse.xml (original)
+++ jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.parse.xml Sun Sep 11 16:58:43 2005
@@ -51,6 +51,7 @@
       <construct class="org.apache.tapestry.services.impl.SpecificationSourceImpl">
         <set-service property="parser" service-id="SpecificationParser"/>
         <set-object property="specification" value="infrastructure:applicationSpecification"/>
+        <set-object property="assetSource" value="service:tapestry.asset.AssetSource"/>
         <event-listener service-id="tapestry.ResetEventCoordinator"/>
       </construct>
     </invoke-factory>

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/INamespace.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/INamespace.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/INamespace.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/INamespace.java Sun Sep 11 16:58:43 2005
@@ -148,8 +148,8 @@
      * Returns the path for the named component (within the namespace).
      * 
      * @param type
-     *            the component alias
-     * @return the specification path of the component
+     *            the component type
+     * @return the specification for the component
      * @throws ApplicationRuntimeException
      *             if the specification doesn't exist or can't be loaded
      */

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetFactory.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetFactory.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetFactory.java Sun Sep 11 16:58:43 2005
@@ -43,7 +43,8 @@
      * @param locale
      *            the desired locale of the asset; the closest match will be used.
      * @param location
-     *            the location to be associated with the returned asset.
+     *            the location to be associated with the returned asset, or null to not attempt to
+     *            localize the asset
      * @throws org.apache.hivemind.ApplicationRuntimeException
      *             if no matching asset may be found.
      */

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSource.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSource.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSource.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSource.java Sun Sep 11 16:58:43 2005
@@ -21,7 +21,7 @@
 import org.apache.tapestry.IAsset;
 
 /**
- * Used to create an {@link org.apache.tapestry.IAsset}instance for a particular asset path. The
+ * Used to create an {@link org.apache.tapestry.IAsset} instance for a particular asset path. The
  * path may have a prefix that indicates its type, or it may be relative to some existing resource.
  * 
  * @author Howard M. Lewis Ship
@@ -29,5 +29,24 @@
  */
 public interface AssetSource
 {
+    /**
+     * Finds an asset relative to some existing resource (typically, a page, component or library
+     * specification).
+     * 
+     * @param base
+     *            the base resource used for resolving the asset
+     * @param path
+     *            the path relative to the base resource; alternately, the path may include a prefix
+     *            that defines a domain (such as "classpath:" or "context:") in which case the base
+     *            resource is ignored and the resource resolved within that domain
+     * @param locale
+     *            used to find a localized version of the asset, may be null to indicate no
+     *            localization
+     * @param location
+     *            used to report errors (such as missing resources)
+     * @return the asset, possibly localized
+     * @throws org.apache.hivemind.ApplicationRuntimeException
+     *             if the asset does not exist
+     */
     public IAsset findAsset(Resource base, String path, Locale locale, Location location);
 }

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSourceImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSourceImpl.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSourceImpl.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetSourceImpl.java Sun Sep 11 16:58:43 2005
@@ -56,7 +56,6 @@
     {
         Defense.notNull(base, "base");
         Defense.notNull(path, "path");
-        Defense.notNull(locale, "locale");
         Defense.notNull(location, "location");
 
         int colonx = path.indexOf(':');

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ContextAssetFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ContextAssetFactory.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ContextAssetFactory.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ContextAssetFactory.java Sun Sep 11 16:58:43 2005
@@ -17,9 +17,12 @@
 import java.util.Locale;
 
 import org.apache.hivemind.ApplicationRuntimeException;
+import org.apache.hivemind.ClassResolver;
 import org.apache.hivemind.Location;
 import org.apache.hivemind.Resource;
+import org.apache.hivemind.util.ClasspathResource;
 import org.apache.tapestry.IAsset;
+import org.apache.tapestry.engine.IEngineService;
 import org.apache.tapestry.web.WebContext;
 import org.apache.tapestry.web.WebContextResource;
 
@@ -38,6 +41,10 @@
 
     private Resource _servletRoot;
 
+    private ClassResolver _classResolver;
+
+    private IEngineService _assetService;
+
     public void initializeService()
     {
         _servletRoot = new WebContextResource(_context, "/");
@@ -46,6 +53,18 @@
     public IAsset createAsset(Resource baseResource, String path, Locale locale, Location location)
     {
         Resource assetResource = _servletRoot.getRelativeResource(path);
+
+        // Here's the thing; In Tapestry 3.0 and earlier, you could specify
+        // library path like /org/apache/tapestry/contrib/Contrib.library. In the new scheme
+        // of things, that should be "classpath:/org/apache/tapestry/contrib/Contrib.library".
+        // But to keep a lot of things from breaking, we'll kludgely allow that here.
+
+        if (assetResource.getResourceURL() == null && path.startsWith("/"))
+        {
+            ClasspathResource resource = new ClasspathResource(_classResolver, path);
+            return new PrivateAsset(resource, _assetService, location);
+        }
+
         Resource localized = assetResource.getLocalization(locale);
 
         if (localized == null)
@@ -68,5 +87,15 @@
     public void setContextPath(String contextPath)
     {
         _contextPath = contextPath;
+    }
+
+    public void setAssetService(IEngineService assetService)
+    {
+        _assetService = assetService;
+    }
+
+    public void setClassResolver(ClassResolver classResolver)
+    {
+        _classResolver = classResolver;
     }
 }

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/AbstractBinding.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/AbstractBinding.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/AbstractBinding.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/AbstractBinding.java Sun Sep 11 16:58:43 2005
@@ -129,4 +129,30 @@
     {
         return _valueConverter;
     }
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(getClass().getName());
+        buffer.append("@");
+        buffer.append(Integer.toHexString(hashCode()));
+        buffer.append("[");
+        buffer.append(_description);
+
+        extendDescription(buffer);
+
+        buffer.append(", location=");
+        buffer.append(_location);
+        buffer.append("]");
+
+        return buffer.toString();
+    }
+
+    /**
+     * Does nothing, subclasses may override to add additional information.
+     */
+    protected void extendDescription(StringBuffer buffer)
+    {
+
+    }
 }

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingMessages.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingMessages.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingMessages.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingMessages.java Sun Sep 11 16:58:43 2005
@@ -40,4 +40,13 @@
     {
         return _formatter.format("missing-asset", component.getExtendedId(), assetName);
     }
+
+    static String listenerMethodFailure(IComponent component, String methodName, Throwable cause)
+    {
+        return _formatter.format(
+                "listener-method-failure",
+                methodName,
+                component.getExtendedId(),
+                cause);
+    }
 }

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingStrings.properties
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingStrings.properties?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingStrings.properties (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/BindingStrings.properties Sun Sep 11 16:58:43 2005
@@ -14,4 +14,5 @@
 
 convert-object-error=Error converting value for {0}: {1}
 read-only-binding=Binding for {0} ({1}) may not be updated.
-missing-asset=Component {0} does not contain an asset named ''{1}''.
\ No newline at end of file
+missing-asset=Component {0} does not contain an asset named ''{1}''.
+listener-method-failure=Exception invoking listener method {0} of component {1}: {2}
\ No newline at end of file

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerMethodBinding.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerMethodBinding.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerMethodBinding.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerMethodBinding.java Sun Sep 11 16:58:43 2005
@@ -16,19 +16,30 @@
 
 import org.apache.hivemind.Location;
 import org.apache.hivemind.util.Defense;
+import org.apache.tapestry.BindingException;
+import org.apache.tapestry.IActionListener;
 import org.apache.tapestry.IComponent;
+import org.apache.tapestry.IRequestCycle;
+import org.apache.tapestry.PageRedirectException;
 import org.apache.tapestry.coerce.ValueConverter;
 
 /**
  * @author Howard M. Lewis Ship
  * @since 4.0
  */
-public class ListenerMethodBinding extends AbstractBinding
+public class ListenerMethodBinding extends AbstractBinding implements IActionListener
 {
     private final IComponent _component;
 
     private final String _methodName;
 
+    // We have to defer obtaining the listener until after the page is loaded, because it is
+    // (currently) reliant on the page's engine property to gain access to the
+    // ListenerMapSource. I'd prefer it if this was a final field, resolved by the constructor,
+    // but that will involve injecting the ListenerMapSource into AbstractComponent.
+
+    private IActionListener _listener;
+
     public ListenerMethodBinding(IComponent component, String methodName, String description,
             ValueConverter valueConverter, Location location)
     {
@@ -46,9 +57,43 @@
         return _component;
     }
 
+    /**
+     * Returns this binding object; the binding object delegates to the actual listener. This allows
+     * us to intercept errors and report the location of the binding.
+     */
     public Object getObject()
     {
-        return _component.getListeners().getListener(_methodName);
+        return this;
+    }
+
+    public void actionTriggered(IComponent component, IRequestCycle cycle)
+    {
+        try
+        {
+            if (_listener == null)
+                _listener = _component.getListeners().getListener(_methodName);
+
+            _listener.actionTriggered(component, cycle);
+        }
+        catch (PageRedirectException ex)
+        {
+            throw ex;
+        }
+        catch (RuntimeException ex)
+        {
+            throw new BindingException(BindingMessages.listenerMethodFailure(
+                    _component,
+                    _methodName,
+                    ex), _component, getLocation(), this, ex);
+        }
+    }
+
+    protected void extendDescription(StringBuffer buffer)
+    {
+        buffer.append(", component=");
+        buffer.append(_component.getExtendedId());
+        buffer.append(", methodName=");
+        buffer.append(_methodName);
     }
 
 }

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/ISpecificationSource.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/ISpecificationSource.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/ISpecificationSource.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/ISpecificationSource.java Sun Sep 11 16:58:43 2005
@@ -20,75 +20,63 @@
 import org.apache.tapestry.spec.ILibrarySpecification;
 
 /**
- *  Defines access to component specifications.
- *
- *  @see IComponentSpecification
- *
- *  @author Howard Lewis Ship
+ * Defines access to component specifications.
  * 
- **/
+ * @see IComponentSpecification
+ * @author Howard Lewis Ship
+ */
 
 public interface ISpecificationSource
 {
     /**
-     *  Retrieves a component specification, parsing it as necessary.
-     *  
-     *  @param specificationLocation the location where the specification
-     *  may be read from.
+     * Retrieves a component specification, parsing it as necessary.
      * 
-     *  @throws org.apache.tapestry.ApplicationRuntimeException if the specification doesn't
-     *  exist, is unreadable or invalid.
-     * 
-     *  @since 2.2
-     * 
-     **/
+     * @param specificationLocation
+     *            the location where the specification may be read from.
+     * @throws org.apache.hivemind.ApplicationRuntimeException
+     *             if the specification doesn't exist, is unreadable or invalid.
+     * @since 2.2
+     */
 
     public IComponentSpecification getComponentSpecification(Resource specificationLocation);
 
     /**
-     *  Retrieves a component specification, parsing it as necessary.
-     *  
-     *  @param specificationLocation the location where the specification
-     *  may be read from.
-     * 
-     *  @throws org.apache.tapestry.ApplicationRuntimeException if the specification doesn't
-     *  exist, is unreadable or invalid.
-     * 
-     *  @since 2.2
+     * Retrieves a page specification, parsing it as necessary.
      * 
-     **/
+     * @param specificationLocation
+     *            the location where the specification may be read from.
+     * @throws org.apache.hivemind.ApplicationRuntimeException
+     *             if the specification doesn't exist, is unreadable or invalid.
+     * @since 2.2
+     */
 
     public IComponentSpecification getPageSpecification(Resource specificationLocation);
 
     /**
-     *  Returns a {@link org.apache.tapestry.spec.LibrarySpecification} with the given path.
+     * Returns a {@link org.apache.tapestry.spec.LibrarySpecification} with the given path.
      * 
-     *  @param specificationLocation the resource path of the specification
-     *  to return
-     *  @throws org.apache.tapestry.ApplicationRuntimeException if the specification
-     *  cannot be read
-     * 
-     *  @since 2.2
-     * 
-     **/
+     * @param specificationLocation
+     *            the resource path of the specification to return
+     * @throws org.apache.hivemind.ApplicationRuntimeException
+     *             if the specification cannot be read
+     * @since 2.2
+     */
 
     public ILibrarySpecification getLibrarySpecification(Resource specificationLocation);
 
     /**
-     *  Returns the {@link INamespace} for the application.
+     * Returns the {@link INamespace} for the application.
      * 
-     *  @since 2.2
-     * 
-     **/
+     * @since 2.2
+     */
 
     public INamespace getApplicationNamespace();
 
     /**
-     *  Returns the {@link INamespace} for the framework itself.
-     * 
-     *  @since 2.2
+     * Returns the {@link INamespace} for the framework itself.
      * 
-     **/
+     * @since 2.2
+     */
 
     public INamespace getFrameworkNamespace();
 }

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/Namespace.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/Namespace.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/Namespace.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/engine/Namespace.java Sun Sep 11 16:58:43 2005
@@ -23,18 +23,18 @@
 import java.util.Set;
 
 import org.apache.hivemind.ApplicationRuntimeException;
-import org.apache.hivemind.ClassResolver;
 import org.apache.hivemind.Location;
 import org.apache.hivemind.Resource;
-import org.apache.hivemind.util.ClasspathResource;
 import org.apache.tapestry.INamespace;
 import org.apache.tapestry.Tapestry;
+import org.apache.tapestry.services.NamespaceResources;
 import org.apache.tapestry.spec.IComponentSpecification;
 import org.apache.tapestry.spec.ILibrarySpecification;
 
 /**
- * Implementation of {@link org.apache.tapestry.INamespace}that works with a
- * {@link ISpecificationSource}to obtain page and component specifications as needed.
+ * Implementation of {@link org.apache.tapestry.INamespace} that works with a
+ * {@link org.apache.tapestry.services.NamespaceResources} to obtain page and component
+ * specifications as needed.
  * 
  * @author Howard Lewis Ship
  * @since 2.2
@@ -42,23 +42,21 @@
 
 public class Namespace implements INamespace
 {
-    private ILibrarySpecification _specification;
+    private final ILibrarySpecification _specification;
 
-    private ISpecificationSource _specificationSource;
-
-    private String _id;
+    private final String _id;
 
     private String _extendedId;
 
-    private INamespace _parent;
+    private final INamespace _parent;
 
-    private boolean _frameworkNamespace;
+    private final boolean _frameworkNamespace;
 
-    private boolean _applicationNamespace;
+    private final boolean _applicationNamespace;
 
     /** @since 4.0 */
 
-    private ClassResolver _resolver;
+    private final NamespaceResources _resources;
 
     /**
      * Map of {@link org.apache.tapestry.spec.ComponentSpecification}keyed on page name. The map is
@@ -66,28 +64,27 @@
      * page discovery in the application namespace).
      */
 
-    private Map _pages = Collections.synchronizedMap(new HashMap());
+    private final Map _pages = Collections.synchronizedMap(new HashMap());
 
     /**
      * Map of {@link org.apache.tapestry.spec.ComponentSpecification}keyed on component alias.
      */
 
-    private Map _components = Collections.synchronizedMap(new HashMap());
+    private final Map _components = Collections.synchronizedMap(new HashMap());
 
     /**
      * Map, keyed on id, of {@link INamespace}.
      */
 
-    private Map _children = Collections.synchronizedMap(new HashMap());
+    private final Map _children = Collections.synchronizedMap(new HashMap());
 
     public Namespace(String id, INamespace parent, ILibrarySpecification specification,
-            ISpecificationSource specificationSource, ClassResolver resolver)
+            NamespaceResources resources)
     {
         _id = id;
         _parent = parent;
         _specification = specification;
-        _specificationSource = specificationSource;
-        _resolver = resolver;
+        _resources = resources;
 
         _applicationNamespace = (_id == null);
         _frameworkNamespace = FRAMEWORK_NAMESPACE.equals(_id);
@@ -260,9 +257,11 @@
                     name,
                     getNamespaceId()));
 
-        Resource location = getSpecificationLocation().getRelativeResource(path);
+        // We don't record line-precise data about <page> elements
+        // so use the location for the specification as a whole (at least identifying
+        // the right file)
 
-        return _specificationSource.getPageSpecification(location);
+        return _resources.getPageSpecification(getSpecificationLocation(), path, getLocation());
     }
 
     private IComponentSpecification locateComponentSpecification(String type)
@@ -275,9 +274,12 @@
                     type,
                     getNamespaceId()));
 
-        Resource location = getSpecificationLocation().getRelativeResource(path);
+        // We don't record line-precise data about <component-type> elements
+        // so use the location for the specification as a whole (at least identifying
+        // the right file)
 
-        return _specificationSource.getComponentSpecification(location);
+        return _resources
+                .getComponentSpecification(getSpecificationLocation(), path, getLocation());
     }
 
     private INamespace createNamespace(String id)
@@ -290,19 +292,16 @@
                     id,
                     getNamespaceId()));
 
-        Resource location = getSpecificationLocation().getRelativeResource(path);
-
-        // Ok, an absolute path to a library for an application whose specification
-        // is in the context root is problematic, cause getRelativeLocation()
-        // will still be looking in the context. Handle this case with the
-        // following little kludge:
-
-        if (location.getResourceURL() == null && path.startsWith("/"))
-            location = new ClasspathResource(_resolver, path);
-
-        ILibrarySpecification ls = _specificationSource.getLibrarySpecification(location);
+        // We don't record line-precise data about <library> elements
+        // so use the location for the specification as a whole (at least identifying
+        // the right file)
+
+        ILibrarySpecification ls = _resources.findChildLibrarySpecification(
+                getSpecificationLocation(),
+                path,
+                getLocation());
 
-        return new Namespace(id, this, ls, _specificationSource, _resolver);
+        return new Namespace(id, this, ls, _resources);
     }
 
     public synchronized boolean containsPage(String name)

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/SpecificationParser.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/SpecificationParser.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/SpecificationParser.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/SpecificationParser.java Sun Sep 11 16:58:43 2005
@@ -97,13 +97,16 @@
     public static final String BEAN_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
 
     /**
-     * Perl5 pattern for component alias. Letter, followed by letter, number, or underscore. This is
-     * used to validate component types registered in the application or library specifications.
+     * Perl5 pattern for component type (which was known as an "alias" in earlier versions of
+     * Tapestry). This is either a simple property name, or a series of property names seperated by
+     * slashes (the latter being new in Tapestry 4.0). This defines a literal that can appear in a
+     * library or application specification.
      * 
      * @since 2.2
      */
 
-    public static final String COMPONENT_ALIAS_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
+    public static final String COMPONENT_ALIAS_PATTERN = "^(" + IDENTIFIER_PATTERN + "/)*"
+            + IDENTIFIER_PATTERN + "$";
 
     /**
      * Perl5 pattern for component ids. Letter, followed by letter, number or underscore.
@@ -114,14 +117,16 @@
     public static final String COMPONENT_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
 
     /**
-     * Perl5 pattern for component types. Component types are an optional namespace prefix followed
-     * by a normal identifier.
+     * Perl5 pattern for component types (i.e., the type attribute of the &lt;component&gt;
+     * element). Component types are an optional namespace prefix followed by a component type
+     * (within the library defined by the namespace). Starting in 4.0, the type portion is actually
+     * a series of identifiers seperated by slashes.
      * 
      * @since 2.2
      */
 
-    public static final String COMPONENT_TYPE_PATTERN = "^(" + IDENTIFIER_PATTERN + ":)?"
-            + IDENTIFIER_PATTERN + "$";
+    public static final String COMPONENT_TYPE_PATTERN = "^(" + IDENTIFIER_PATTERN + ":)?" + "("
+            + IDENTIFIER_PATTERN + "/)*" + IDENTIFIER_PATTERN + "$";
 
     /**
      * We can share a single map for all the XML attribute to object conversions, since the keys are

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/TemplateParser.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/TemplateParser.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/TemplateParser.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/parse/TemplateParser.java Sun Sep 11 16:58:43 2005
@@ -146,13 +146,14 @@
      * Pattern used to recognize implicit components (whose type is defined in the template).
      * Subgroup 1 is the id (which may be null) and subgroup 2 is the type (which may be qualified
      * with a library prefix). Subgroup 4 is the library id, Subgroup 5 is the simple component
-     * type.
+     * type, which may (as of 4.0) have slashes to delinate folders containing the component.
      * 
      * @since 3.0
      */
 
     public static final String IMPLICIT_ID_PATTERN = "^(" + PROPERTY_NAME_PATTERN + ")?@((("
-            + PROPERTY_NAME_PATTERN + "):)?(" + PROPERTY_NAME_PATTERN + "))$";
+            + PROPERTY_NAME_PATTERN + "):)?((" + PROPERTY_NAME_PATTERN + "/)*"
+            + PROPERTY_NAME_PATTERN + "))$";
 
     private static final int IMPLICIT_ID_PATTERN_ID_GROUP = 1;
 
@@ -756,9 +757,8 @@
                                 _cursor - attributeValueStart);
 
                         attributeEndEvent(_cursor);
-                        
+
                         addAttributeIfUnique(tagName, attributeName, attributeValue);
-                        
 
                         // Advance over the quote.
                         advance();
@@ -778,9 +778,9 @@
                     {
                         String attributeValue = new String(_templateData, attributeValueStart,
                                 _cursor - attributeValueStart);
-                        
+
                         attributeEndEvent(_cursor);
-                        addAttributeIfUnique(tagName, attributeName, attributeValue);                        
+                        addAttributeIfUnique(tagName, attributeName, attributeValue);
 
                         state = WAIT_FOR_ATTRIBUTE_NAME;
                         break;
@@ -975,9 +975,11 @@
             // with a leading dollar sign to ensure no conflicts
             // with user defined component ids (which don't allow dollar signs
             // in the id).
+            // New for 4.0: the component type may included slashes ('/'), but these
+            // are not valid identifiers, so we convert them to '$'.
 
             if (jwcId == null)
-                jwcId = _idAllocator.allocateId("$" + simpleType);
+                jwcId = _idAllocator.allocateId("$" + simpleType.replace('/', '$'));
 
             try
             {

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/NamespaceResources.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/NamespaceResources.java?rev=280219&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/NamespaceResources.java (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/NamespaceResources.java Sun Sep 11 16:58:43 2005
@@ -0,0 +1,82 @@
+// Copyright 2005 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services;
+
+import org.apache.hivemind.Location;
+import org.apache.hivemind.Resource;
+import org.apache.tapestry.spec.IComponentSpecification;
+import org.apache.tapestry.spec.ILibrarySpecification;
+
+/**
+ * Companion to the standard {@link org.apache.tapestry.engine.Namespace implementation} of
+ * {@link org.apache.tapestry.INamespace}. Defines resources needed by the Namespace instance to
+ * operate (these have grown numerous!)
+ * 
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public interface NamespaceResources
+{
+    /**
+     * Finds a child library specification for some parent library specification.
+     * 
+     * @param libraryResource
+     *            the {@link Resource} from which the parent library (or application) specification
+     *            was loaded
+     * @param path
+     *            the relative path from the parent specification resource to the library
+     *            specification. As a special case, a path starting with a leading slash is assumed
+     *            to be on the classpath.
+     * @param location TODO
+     * @return the library specification.
+     */
+    ILibrarySpecification findChildLibrarySpecification(Resource libraryResource, String path, Location location);
+
+    /**
+     * Retrieves a page specification, parsing it as necessary.
+     * 
+     * @param libraryResource
+     *            the base resource for resolving the path to the page specification; this will be
+     *            the resource for the library (or application) specification
+     * @param specificationPath
+     *            the path to the specification to be parsed
+     * @param location
+     *            used to report errors
+     * @throws org.apache.hivemind.ApplicationRuntimeException
+     *             if the specification doesn't exist, is unreadable or invalid.
+     * @see org.apache.tapestry.engine.ISpecificationSource#getPageSpecification(Resource)
+     */
+
+    IComponentSpecification getPageSpecification(Resource libraryResource,
+            String specificationPath, Location location);
+
+    /**
+     * Retrieves a component specification, parsing it as necessary.
+     * 
+     * @param libraryResource
+     *            the base resource for resolving the path to the page specification; this will be
+     *            the resource for the library (or application) specification
+     * @param specificationPath
+     *            the path to the specification to be parsed
+     * @param location
+     *            used to report errors
+     * @throws org.apache.hivemind.ApplicationRuntimeException
+     *             if the specification doesn't exist, is unreadable or invalid.
+     */
+
+    public IComponentSpecification getComponentSpecification(Resource libraryResource,
+            String specificationPath, Location location);
+
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/NamespaceResourcesImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/NamespaceResourcesImpl.java?rev=280219&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/NamespaceResourcesImpl.java (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/NamespaceResourcesImpl.java Sun Sep 11 16:58:43 2005
@@ -0,0 +1,90 @@
+// Copyright 2005 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services.impl;
+
+import org.apache.hivemind.Location;
+import org.apache.hivemind.Resource;
+import org.apache.hivemind.util.Defense;
+import org.apache.tapestry.IAsset;
+import org.apache.tapestry.asset.AssetSource;
+import org.apache.tapestry.engine.ISpecificationSource;
+import org.apache.tapestry.services.NamespaceResources;
+import org.apache.tapestry.spec.IComponentSpecification;
+import org.apache.tapestry.spec.ILibrarySpecification;
+
+/**
+ * Implementation of {@link org.apache.tapestry.services.NamespaceResources}.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class NamespaceResourcesImpl implements NamespaceResources
+{
+    private final ISpecificationSource _specificationSource;
+
+    private final AssetSource _assetSource;
+
+    public NamespaceResourcesImpl(ISpecificationSource source, AssetSource assetSource)
+    {
+        Defense.notNull(source, "source");
+        Defense.notNull(assetSource, "assetSource");
+
+        _specificationSource = source;
+        _assetSource = assetSource;
+    }
+
+    public ILibrarySpecification findChildLibrarySpecification(Resource parentResource,
+            String path, Location location)
+    {
+        Resource childResource = findSpecificationResource(parentResource, path, location);
+
+        return _specificationSource.getLibrarySpecification(childResource);
+
+    }
+
+    private Resource findSpecificationResource(Resource libraryResource, String path,
+            Location location)
+    {
+        // TODO: This is where we'll play with assets and asset prefixes
+
+        IAsset childAsset = _assetSource.findAsset(libraryResource, path, null, location);
+
+        Resource childResource = childAsset.getResourceLocation();
+
+        return childResource;
+    }
+
+    public IComponentSpecification getPageSpecification(Resource resource,
+            String specificationPath, Location location)
+    {
+        Resource pageSpecificationResource = findSpecificationResource(
+                resource,
+                specificationPath,
+                location);
+
+        return _specificationSource.getPageSpecification(pageSpecificationResource);
+    }
+
+    public IComponentSpecification getComponentSpecification(Resource resource,
+            String specificationPath, Location location)
+    {
+        Resource componentSpecificationResource = findSpecificationResource(
+                resource,
+                specificationPath,
+                location);
+
+        return _specificationSource.getComponentSpecification(componentSpecificationResource);
+    }
+
+}

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/SpecificationSourceImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/SpecificationSourceImpl.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/SpecificationSourceImpl.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/services/impl/SpecificationSourceImpl.java Sun Sep 11 16:58:43 2005
@@ -22,10 +22,12 @@
 import org.apache.hivemind.Resource;
 import org.apache.hivemind.util.ClasspathResource;
 import org.apache.tapestry.INamespace;
+import org.apache.tapestry.asset.AssetSource;
 import org.apache.tapestry.engine.ISpecificationSource;
 import org.apache.tapestry.engine.Namespace;
 import org.apache.tapestry.event.ResetEventListener;
 import org.apache.tapestry.parse.ISpecificationParser;
+import org.apache.tapestry.services.NamespaceResources;
 import org.apache.tapestry.spec.IApplicationSpecification;
 import org.apache.tapestry.spec.IComponentSpecification;
 import org.apache.tapestry.spec.ILibrarySpecification;
@@ -33,15 +35,12 @@
 import org.apache.tapestry.util.xml.DocumentParseException;
 
 /**
- *  Default implementation of {@link ISpecificationSource} that
- *  expects to use the normal class loader to locate component
- *  specifications from within the classpath.
- *
- * <p>Caches specifications in memory forever, or until {@link #resetDidOccur()()} is invoked.
- *
- *
- * @author Howard Lewis Ship
+ * Default implementation of {@link ISpecificationSource} that expects to use the normal class
+ * loader to locate component specifications from within the classpath.
+ * <p>
+ * Caches specifications in memory forever, or until {@link #resetDidOccur()()} is invoked.
  * 
+ * @author Howard Lewis Ship
  */
 
 public class SpecificationSourceImpl implements ISpecificationSource, ResetEventListener
@@ -49,48 +48,53 @@
     private ClassResolver _classResolver;
 
     private IApplicationSpecification _specification;
+
     private ISpecificationParser _parser;
 
+    private NamespaceResources _namespaceResources;
+
     private INamespace _applicationNamespace;
+
     private INamespace _frameworkNamespace;
 
+    private AssetSource _assetSource;
+
     /**
-     *  Contains previously parsed component specifications.
-     *
+     * Contains previously parsed component specifications.
      */
 
     private Map _componentCache = new HashMap();
 
     /**
-     *  Contains previously parsed page specifications.
-     * 
-     *  @since 2.2
+     * Contains previously parsed page specifications.
      * 
+     * @since 2.2
      */
 
     private Map _pageCache = new HashMap();
 
     /**
-     *  Contains previously parsed library specifications, keyed
-     *  on specification resource path.
-     * 
-     *  @since 2.2
+     * Contains previously parsed library specifications, keyed on specification resource path.
      * 
+     * @since 2.2
      */
 
     private Map _libraryCache = new HashMap();
 
     /**
-     *  Contains {@link INamespace} instances, keyed on id (which will
-     *  be null for the application specification).
-     * 
+     * Contains {@link INamespace} instances, keyed on id (which will be null for the application
+     * specification).
      */
 
     private Map _namespaceCache = new HashMap();
 
+    public void initializeService()
+    {
+        _namespaceResources = new NamespaceResourcesImpl(this, _assetSource);
+    }
+
     /**
-     *  Clears the specification cache.  This is used during debugging.
-     *
+     * Clears the specification cache. This is used during debugging.
      */
 
     public synchronized void resetEventDidOccur()
@@ -118,8 +122,7 @@
         catch (DocumentParseException ex)
         {
             throw new ApplicationRuntimeException(
-                ImplMessages.unableToParseSpecification(resource),
-                ex);
+                    ImplMessages.unableToParseSpecification(resource), ex);
         }
 
         return result;
@@ -134,24 +137,24 @@
         catch (DocumentParseException ex)
         {
             throw new ApplicationRuntimeException(
-                ImplMessages.unableToParseSpecification(resource),
-                ex);
+                    ImplMessages.unableToParseSpecification(resource), ex);
         }
 
     }
 
     /**
-     *  Gets a component specification.
-     * 
-     *  @param resourcePath the complete resource path to the specification.
-     *  @throws ApplicationRuntimeException if the specification cannot be obtained.
+     * Gets a component specification.
      * 
+     * @param resourcePath
+     *            the complete resource path to the specification.
+     * @throws ApplicationRuntimeException
+     *             if the specification cannot be obtained.
      */
 
     public synchronized IComponentSpecification getComponentSpecification(Resource resourceLocation)
     {
-        IComponentSpecification result =
-            (IComponentSpecification) _componentCache.get(resourceLocation);
+        IComponentSpecification result = (IComponentSpecification) _componentCache
+                .get(resourceLocation);
 
         if (result == null)
         {
@@ -193,7 +196,7 @@
     public synchronized INamespace getApplicationNamespace()
     {
         if (_applicationNamespace == null)
-            _applicationNamespace = new Namespace(null, null, _specification, this, _classResolver);
+            _applicationNamespace = new Namespace(null, null, _specification, _namespaceResources);
 
         return _applicationNamespace;
     }
@@ -202,12 +205,13 @@
     {
         if (_frameworkNamespace == null)
         {
-            Resource frameworkLocation =
-                new ClasspathResource(_classResolver, "/org/apache/tapestry/Framework.library");
+            Resource resource = new ClasspathResource(_classResolver,
+                    "/org/apache/tapestry/Framework.library");
 
-            ILibrarySpecification ls = getLibrarySpecification(frameworkLocation);
+            ILibrarySpecification ls = getLibrarySpecification(resource);
 
-            _frameworkNamespace = new Namespace(INamespace.FRAMEWORK_NAMESPACE, null, ls, this, _classResolver);
+            _frameworkNamespace = new Namespace(INamespace.FRAMEWORK_NAMESPACE, null, ls,
+                    _namespaceResources);
         }
 
         return _frameworkNamespace;
@@ -226,5 +230,10 @@
     public void setSpecification(IApplicationSpecification specification)
     {
         _specification = specification;
+    }
+
+    public void setAssetSource(AssetSource assetSource)
+    {
+        _assetSource = assetSource;
     }
 }

Modified: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/BindingTestCase.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/BindingTestCase.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/BindingTestCase.java (original)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/BindingTestCase.java Sun Sep 11 16:58:43 2005
@@ -14,9 +14,9 @@
 
 package org.apache.tapestry.binding;
 
+import org.apache.tapestry.BaseComponentTestCase;
 import org.apache.tapestry.IComponent;
 import org.apache.tapestry.coerce.ValueConverter;
-import org.apache.tapestry.junit.TapestryTestCase;
 import org.easymock.MockControl;
 
 /**
@@ -25,7 +25,7 @@
  * @author Howard M. Lewis Ship
  * @since 4.0
  */
-public abstract class BindingTestCase extends TapestryTestCase
+public abstract class BindingTestCase extends BaseComponentTestCase
 {
     protected IComponent newComponent(String extendedId)
     {

Modified: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/TestListenerMethodBinding.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/TestListenerMethodBinding.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/TestListenerMethodBinding.java (original)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/binding/TestListenerMethodBinding.java Sun Sep 11 16:58:43 2005
@@ -15,11 +15,13 @@
 package org.apache.tapestry.binding;
 
 import org.apache.hivemind.Location;
+import org.apache.tapestry.BindingException;
 import org.apache.tapestry.IActionListener;
 import org.apache.tapestry.IComponent;
+import org.apache.tapestry.IRequestCycle;
+import org.apache.tapestry.PageRedirectException;
 import org.apache.tapestry.coerce.ValueConverter;
 import org.apache.tapestry.listener.ListenerMap;
-import org.easymock.MockControl;
 
 /**
  * Test for {@link org.apache.tapestry.binding.ListenerMethodBinding}.
@@ -29,33 +31,160 @@
  */
 public class TestListenerMethodBinding extends BindingTestCase
 {
-    public void testGetObject()
+    public void testInvokeListener()
     {
-        MockControl cc = newControl(IComponent.class);
-        IComponent component = (IComponent) cc.getMock();
+        IComponent component = newComponent();
+        ListenerMap map = newListenerMap();
+        IActionListener listener = newListener();
+        Location l = newLocation();
+        IComponent sourceComponent = newComponent();
+        IRequestCycle cycle = newCycle();
+        ValueConverter vc = newValueConverter();
 
-        MockControl lmc = newControl(ListenerMap.class);
-        ListenerMap lm = (ListenerMap) lmc.getMock();
+        trainGetListener(component, map, listener);
 
-        IActionListener listener = (IActionListener) newMock(IActionListener.class);
+        listener.actionTriggered(sourceComponent, cycle);
 
-        component.getListeners();
-        cc.setReturnValue(lm);
+        replayControls();
+
+        ListenerMethodBinding b = new ListenerMethodBinding(component, "foo", "param", vc, l);
+
+        assertSame(b, b.getObject());
+        assertSame(component, b.getComponent());
+
+        b.actionTriggered(sourceComponent, cycle);
 
-        lm.getListener("foo");
-        lmc.setReturnValue(listener);
+        verifyControls();
+    }
 
+    public void testToString()
+    {
+        IComponent component = newComponent();
+        Location l = newLocation();
         ValueConverter vc = newValueConverter();
 
+        trainGetExtendedId(component, "Fred/barney");
+
+        replayControls();
+
+        ListenerMethodBinding b = new ListenerMethodBinding(component, "foo", "param", vc, l);
+
+        String toString = b.toString();
+        String description = toString.substring(toString.indexOf('[') + 1, toString.length() - 1);
+
+        assertEquals(
+                "param, component=Fred/barney, methodName=foo, location=classpath:/org/apache/tapestry/binding/TestListenerMethodBinding, line 1",
+                description);
+
+        verifyControls();
+    }
+
+    public void testInvokeAndRedirect()
+    {
+        IComponent component = newComponent();
+        ListenerMap map = newListenerMap();
+        IActionListener listener = newListener();
         Location l = newLocation();
+        ValueConverter vc = newValueConverter();
+        IComponent sourceComponent = newComponent();
+        IRequestCycle cycle = newCycle();
+
+        trainGetListener(component, map, listener);
+
+        listener.actionTriggered(sourceComponent, cycle);
+
+        Throwable t = new PageRedirectException("TargetPage");
+        getControl(listener).setThrowable(t);
 
         replayControls();
 
         ListenerMethodBinding b = new ListenerMethodBinding(component, "foo", "param", vc, l);
 
-        assertSame(listener, b.getObject());
-        assertSame(component, b.getComponent());
+        try
+        {
+            b.actionTriggered(sourceComponent, cycle);
+            unreachable();
+        }
+        catch (PageRedirectException ex)
+        {
+            assertSame(t, ex);
+        }
+
+        verifyControls();
+
+    }
+
+    public void testInvokeListenerFailure()
+    {
+        IComponent component = newComponent();
+        ListenerMap map = newListenerMap();
+        IActionListener listener = newListener();
+        Location l = newLocation();
+        ValueConverter vc = newValueConverter();
+        IComponent sourceComponent = newComponent();
+        IRequestCycle cycle = newCycle();
+
+        trainGetListener(component, map, listener);
+
+        listener.actionTriggered(sourceComponent, cycle);
+
+        Throwable t = new RuntimeException("Failure.");
+        getControl(listener).setThrowable(t);
+
+        trainGetExtendedId(component, "Fred/barney");
+
+        replayControls();
+
+        ListenerMethodBinding b = new ListenerMethodBinding(component, "foo", "param", vc, l);
+
+        try
+        {
+            b.actionTriggered(sourceComponent, cycle);
+            unreachable();
+        }
+        catch (BindingException ex)
+        {
+            assertEquals(
+                    "Exception invoking listener method foo of component Fred/barney: Failure.",
+                    ex.getMessage());
+            assertSame(component, ex.getComponent());
+            assertSame(l, ex.getLocation());
+            assertSame(b, ex.getBinding());
+        }
 
         verifyControls();
+    }
+
+    private void trainGetListener(IComponent component, ListenerMap lm, IActionListener listener)
+    {
+        trainGetListeners(component, lm);
+        trainGetListener(lm, "foo", listener);
+    }
+
+    protected IRequestCycle newCycle()
+    {
+        return (IRequestCycle) newMock(IRequestCycle.class);
+    }
+
+    private void trainGetListener(ListenerMap map, String methodName, IActionListener listener)
+    {
+        map.getListener(methodName);
+        getControl(map).setReturnValue(listener);
+    }
+
+    private void trainGetListeners(IComponent component, ListenerMap lm)
+    {
+        component.getListeners();
+        getControl(component).setReturnValue(lm);
+    }
+
+    private ListenerMap newListenerMap()
+    {
+        return (ListenerMap) newMock(ListenerMap.class);
+    }
+
+    private IActionListener newListener()
+    {
+        return (IActionListener) newMock(IActionListener.class);
     }
 }

Modified: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/TestComponentMessages.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/TestComponentMessages.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/TestComponentMessages.java (original)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/TestComponentMessages.java Sun Sep 11 16:58:43 2005
@@ -150,7 +150,7 @@
 
         IPage page = newPage(spec, source, locale);
 
-        INamespace namespace = new Namespace(null, null, newLibrarySpec(), null, null);
+        INamespace namespace = new Namespace(null, null, newLibrarySpec(), null);
 
         page.setNamespace(namespace);
 
@@ -301,7 +301,7 @@
         IComponentSpecification spec = newSpec(MOCK1);
         spec.setLocation(new CreatorLocation());
 
-        INamespace namespace = new Namespace(null, null, newLibrarySpec(), null, null);
+        INamespace namespace = new Namespace(null, null, newLibrarySpec(), null);
 
         IPage page = newPage(spec, source, new Locale("fr"));
         page.setNamespace(namespace);

Added: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.html
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.html?rev=280219&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.html (added)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.html Sun Sep 11 16:58:43 2005
@@ -0,0 +1,4 @@
+Tests for implicit components that have slashes in the component type.
+
+<span jwcid="@foo/Bar"/>
+<span jwcid="baz@biff/bop/Boop"/>
\ No newline at end of file

Added: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.jwc
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.jwc?rev=280219&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.jwc (added)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.jwc Sun Sep 11 16:58:43 2005
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- 
+   Copyright 2005 The Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<!DOCTYPE component-specification PUBLIC 
+  "-//Apache Software Foundation//Tapestry Specification 4.0//EN" 
+  "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
+<component-specification>
+    <component id="fred" type="rubble/Barney"/>    
+</component-specification>
\ No newline at end of file

Added: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.library
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.library?rev=280219&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.library (added)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/SlashInComponentType.library Sun Sep 11 16:58:43 2005
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!-- 
+   Copyright 2005 The Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<!DOCTYPE library-specification PUBLIC 
+  "-//Apache Software Foundation//Tapestry Specification 4.0//EN" 
+  "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
+     	
+<library-specification>
+
+  <component-type
+   type="foo/Bar"
+   specification-path="foocomponents/BarComponent.jwc"/>
+
+</library-specification>

Modified: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestSpecificationParser.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestSpecificationParser.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestSpecificationParser.java (original)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestSpecificationParser.java Sun Sep 11 16:58:43 2005
@@ -1098,4 +1098,30 @@
 
         assertEquals(true, cs.isDeprecated());
     }
+
+    /** @since 4.0 */
+
+    public void testLibrarySlashInComponentType() throws Exception
+    {
+        ILibrarySpecification ls = parseLib("SlashInComponentType.library");
+
+        List types = ls.getComponentTypes();
+
+        assertEquals(1, types.size());
+
+        assertEquals("foo/Bar", types.get(0));
+
+        assertEquals("foocomponents/BarComponent.jwc", ls.getComponentSpecificationPath("foo/Bar"));
+    }
+
+    /** @since 4.0 */
+
+    public void testComponentSlashInComponentType() throws Exception
+    {
+        IComponentSpecification cs = parseComponent("SlashInComponentType.jwc");
+
+        IContainedComponent cc = cs.getComponent("fred");
+
+        assertEquals("rubble/Barney", cc.getType());
+    }
 }

Modified: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestTemplateParser.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestTemplateParser.java?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestTemplateParser.java (original)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/junit/parse/TestTemplateParser.java Sun Sep 11 16:58:43 2005
@@ -667,4 +667,22 @@
                 "DuplicateTagAttributeSingleQuotes.html",
                 "Tag <input> on line 3 contains more than one 'value' attribute.");
     }
+
+    /** @since 4.0 */
+    public void testSlashInComponentType() throws Exception
+    {
+        TemplateToken[] tokens = run("SlashInComponentType.html", new ParserDelegate());
+
+        assertEquals(6, tokens.length);
+
+        OpenToken token1 = (OpenToken) tokens[1];
+
+        assertEquals("$foo$Bar", token1.getId());
+        assertEquals("foo/Bar", token1.getComponentType());
+
+        OpenToken token2 = (OpenToken) tokens[4];
+
+        assertEquals("baz", token2.getId());
+        assertEquals("biff/bop/Boop", token2.getComponentType());
+    }
 }

Added: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/services/impl/TestNamespaceResources.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/services/impl/TestNamespaceResources.java?rev=280219&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/services/impl/TestNamespaceResources.java (added)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/services/impl/TestNamespaceResources.java Sun Sep 11 16:58:43 2005
@@ -0,0 +1,171 @@
+// Copyright 2005 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services.impl;
+
+import java.net.URL;
+
+import org.apache.hivemind.Location;
+import org.apache.hivemind.Resource;
+import org.apache.hivemind.test.HiveMindTestCase;
+import org.apache.tapestry.IAsset;
+import org.apache.tapestry.asset.AssetSource;
+import org.apache.tapestry.engine.ISpecificationSource;
+import org.apache.tapestry.services.NamespaceResources;
+import org.apache.tapestry.spec.IComponentSpecification;
+import org.apache.tapestry.spec.ILibrarySpecification;
+
+/**
+ * Tests for {@link org.apache.tapestry.services.impl.NamespaceResourcesImpl}.
+ * 
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public class TestNamespaceResources extends HiveMindTestCase
+{
+    protected Resource newResource()
+    {
+        return (Resource) newMock(Resource.class);
+    }
+
+    protected void trainGetRelativeResource(Resource parent, String path, Resource child)
+    {
+        parent.getRelativeResource(path);
+        getControl(parent).setReturnValue(child);
+    }
+
+    protected void trainGetResourceURL(Resource resource, URL url)
+    {
+        resource.getResourceURL();
+        getControl(resource).setReturnValue(url);
+    }
+
+    protected ISpecificationSource newSource()
+    {
+        return (ISpecificationSource) newMock(ISpecificationSource.class);
+    }
+
+    protected ILibrarySpecification newSpec()
+    {
+        return (ILibrarySpecification) newMock(ILibrarySpecification.class);
+    }
+
+    protected AssetSource newAssetSource()
+    {
+        return (AssetSource) newMock(AssetSource.class);
+    }
+
+    public void testFindChildLibrarySpecification()
+    {
+        Resource parent = newResource();
+        Resource child = newResource();
+        ISpecificationSource source = newSource();
+        ILibrarySpecification spec = newSpec();
+        AssetSource assetSource = newAssetSource();
+        Location l = newLocation();
+
+        trainResolveChildResource(assetSource, parent, "foo/bar.library", l, child);
+
+        trainGetLibrarySpecification(source, child, spec);
+
+        replayControls();
+
+        NamespaceResources nr = new NamespaceResourcesImpl(source, assetSource);
+
+        assertSame(spec, nr.findChildLibrarySpecification(parent, "foo/bar.library", l));
+
+        verifyControls();
+
+    }
+
+    protected void trainResolveChildResource(AssetSource assetSource, Resource parent,
+            String childPath, Location location, Resource child)
+    {
+        IAsset asset = newAsset();
+
+        assetSource.findAsset(parent, childPath, null, location);
+        getControl(assetSource).setReturnValue(asset);
+
+        asset.getResourceLocation();
+        getControl(asset).setReturnValue(child);
+    }
+
+    protected IAsset newAsset()
+    {
+        return (IAsset) newMock(IAsset.class);
+    }
+
+    protected URL newURL()
+    {
+        return getClass().getResource("TestNamespaceResources.class");
+    }
+
+    protected void trainGetLibrarySpecification(ISpecificationSource source, Resource resource,
+            ILibrarySpecification spec)
+    {
+        source.getLibrarySpecification(resource);
+        getControl(source).setReturnValue(spec);
+    }
+
+    protected IComponentSpecification newComponentSpec()
+    {
+        return (IComponentSpecification) newMock(IComponentSpecification.class);
+    }
+
+    public void testGetPageSpecification()
+    {
+        Resource libraryResource = newResource();
+        Resource specResource = newResource();
+        IComponentSpecification spec = newComponentSpec();
+        ISpecificationSource source = newSource();
+        AssetSource assetSource = newAssetSource();
+        Location l = newLocation();
+
+        trainResolveChildResource(assetSource, libraryResource, "Foo.page", l, specResource);
+
+        source.getPageSpecification(specResource);
+        getControl(source).setReturnValue(spec);
+
+        replayControls();
+
+        NamespaceResources nr = new NamespaceResourcesImpl(source, assetSource);
+
+        assertSame(spec, nr.getPageSpecification(libraryResource, "Foo.page", l));
+
+        verifyControls();
+    }
+
+    public void testGetComponentSpecification()
+    {
+        Resource libraryResource = newResource();
+        Resource specResource = newResource();
+        IComponentSpecification spec = newComponentSpec();
+        ISpecificationSource source = newSource();
+        AssetSource assetSource = newAssetSource();
+        Location l = newLocation();
+
+        trainResolveChildResource(assetSource, libraryResource, "Foo.jwc", l, specResource);
+
+        source.getComponentSpecification(specResource);
+        getControl(source).setReturnValue(spec);
+
+        replayControls();
+
+        NamespaceResources nr = new NamespaceResourcesImpl(source, assetSource);
+
+        assertSame(spec, nr.getComponentSpecification(libraryResource, "Foo.jwc", l));
+
+        verifyControls();
+    }
+}

Modified: jakarta/tapestry/trunk/status.xml
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/status.xml?rev=280219&r1=280218&r2=280219&view=diff
==============================================================================
--- jakarta/tapestry/trunk/status.xml (original)
+++ jakarta/tapestry/trunk/status.xml Sun Sep 11 16:58:43 2005
@@ -66,6 +66,8 @@
       <action type="fix" dev="MB" fixes-bug="TAPESTRY-467">Document GenericLink component</action>
       <action type="fix" dev="MB" fixes-bug="TAPESTRY-480">Document Rollover component</action>
       <action type="fix" dev="MB" fixes-bug="TAPESTRY-484">Document ServiceLink component</action>
+      <action type="fix" dev="HLS" fixes-bug="TAPESTRY-579">Using component types with slashes in the HTML template fails</action>
+      <action type="fix" dev="HLS" fixes-bug="TAPESTRY-595">Resource prefixes not honored inside &lt;page&gt;'s specification-path attribute</action>
     </release>
     <release version="4.0-beta-6" date="Sep 7 2005">
       <action type="update" dev="HLS" due-to="Henri Yandell">Convert Tapestry repository from CVS to SVN</action>



---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org