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 2006/06/05 18:47:02 UTC

svn commit: r411855 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/internal/event/ main/java/org/apache/tapestry/internal/pageload/ main/java/org/apache/tapestry/internal/parser/ main/java...

Author: hlship
Date: Mon Jun  5 09:47:00 2006
New Revision: 411855

URL: http://svn.apache.org/viewvc?rev=411855&view=rev
Log:
Start work on the page loader

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Renderable.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/event/InvalidationEventHubImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoader.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoaderImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.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/MarkupPageElement.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/pageload/
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/pageload/
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/pageload/Fred.html
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/AttributeToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CDATAToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CommentToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/EndElementToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartComponentToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartElementToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TextToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageElement.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/parser/TemplateParserImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Renderable.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Renderable.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Renderable.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Renderable.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,12 @@
+package org.apache.tapestry;
+
+/**
+ * Base interface for objects that can render markup output using a
+ * {@link org.apache.tapestry.MarkupWriter}.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface Renderable
+{
+    void render(MarkupWriter writer);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/event/InvalidationEventHubImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/event/InvalidationEventHubImpl.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/event/InvalidationEventHubImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/event/InvalidationEventHubImpl.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,41 @@
+package org.apache.tapestry.internal.event;
+
+import static org.apache.tapestry.util.CollectionFactory.newThreadSafeList;
+
+import java.util.List;
+
+import org.apache.tapestry.events.InvalidationEvent;
+import org.apache.tapestry.events.InvalidationListener;
+
+/**
+ * Base implementation class for classes (especially services) that need to manage a list of
+ * {@link org.apache.tapestry.events.InvalidationListener}s.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class InvalidationEventHubImpl implements InvalidationEventHub
+{
+    private final List<InvalidationListener> _listeners = newThreadSafeList();
+
+    /** Fires an {@link InvalidationEvent} to all {@link InvalidationListener listener}s. */
+    protected final void fireInvalidationEvent()
+    {
+        InvalidationEvent invalidationEvent = new InvalidationEvent(this);
+
+        for (InvalidationListener listener : _listeners)
+        {
+            listener.objectWasInvalidated(invalidationEvent);
+        }
+    }
+
+    public final void addInvalidationListener(InvalidationListener listener)
+    {
+        _listeners.add(listener);
+    }
+
+    public final void removeInvalidationListener(InvalidationListener listener)
+    {
+        _listeners.remove(listener);
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSource.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSource.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSource.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,31 @@
+package org.apache.tapestry.internal.pageload;
+
+import java.util.Locale;
+
+import org.apache.tapestry.internal.event.InvalidationEventHub;
+import org.apache.tapestry.internal.parser.ComponentTemplate;
+
+/**
+ * Provides access to cached {@link org.apache.tapestry.internal.parser.ComponentTemplate}s. The
+ * source acts as a invalidation event hub, and will broadcast invalidation events when any loaded
+ * template resource changes. The listener for these invalidation events is the page source, which
+ * stores cached page instances.
+ * <p>
+ * TODO: A more sophisticated, finer grained dependency manager.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface ComponentTemplateSource extends InvalidationEventHub
+{
+    /**
+     * Provides access to a template. The template will be parsed as necessary.
+     * 
+     * @param componentName
+     *            the fully qualified class name of the component
+     * @param locale
+     *            the locale to find the template within
+     * @return the cached template instance, if a template exists, or null if the template does not
+     *         exist
+     */
+    ComponentTemplate getTemplate(String componentName, Locale locale);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImpl.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImpl.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,128 @@
+package org.apache.tapestry.internal.pageload;
+
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.hivemind.ClassResolver;
+import org.apache.hivemind.Resource;
+import org.apache.hivemind.util.ClasspathResource;
+import org.apache.tapestry.events.UpdateEvent;
+import org.apache.tapestry.events.UpdateListener;
+import org.apache.tapestry.internal.annotations.Synchronized;
+import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
+import org.apache.tapestry.internal.parser.ComponentTemplate;
+import org.apache.tapestry.internal.parser.TemplateParser;
+import org.apache.tapestry.internal.util.URLChangeTracker;
+
+/**
+ * Service implementation that manages a cache of parsed component templates.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Synchronized
+public final class ComponentTemplateSourceImpl extends InvalidationEventHubImpl implements
+        ComponentTemplateSource, UpdateListener
+{
+    private ClassResolver _classResolver;
+
+    private TemplateParser _parser;
+
+    /**
+     * Caches from a key (combining component name and locale) to a resource. Often, many different
+     * keys will point to the same resource (i.e., "foo:en_US", "foo:en_UK", and "foo:en" may all be
+     * parsed from the same "foo.html" resource). The resource may end up being null, meaning the
+     * template does not exist in any locale.
+     */
+    private final Map<String, Resource> _templateResources = newMap();
+
+    /**
+     * Cache of parsed templates, keyed on resource.
+     */
+    private final Map<Resource, ComponentTemplate> _templates = newMap();
+
+    private final URLChangeTracker _tracker = new URLChangeTracker();
+
+    /**
+     * Resolves the component name to a {@link Resource} and finds the localization of that resource
+     * (the combination of component name and locale is resolved to a resource). The localized
+     * resource is used as the key to a cache of {@link ComponentTemplate}s.
+     */
+    @Synchronized.Read
+    public ComponentTemplate getTemplate(String componentName, Locale locale)
+    {
+        String key = componentName + ":" + locale.toString();
+
+        if (!_templateResources.containsKey(key))
+            locateTemplateResource(componentName, locale, key);
+
+        Resource resource = _templateResources.get(key);
+
+        // If we were unable to locate the template, then there's no point in trying
+        // to parse it.
+
+        if (resource == null)
+            return null;
+
+        // If we haven't yet parsed the template into the cache, do so now.
+
+        if (!_templates.containsKey(resource))
+            parseTemplate(resource);
+
+        return _templates.get(resource);
+    }
+
+    @Synchronized.Write
+    private void parseTemplate(Resource r)
+    {
+        _tracker.add(r.getResourceURL());
+
+        ComponentTemplate template = _parser.parseTemplate(r);
+
+        _templates.put(r, template);
+    }
+
+    @Synchronized.Write
+    private void locateTemplateResource(String componentName, Locale locale, String key)
+    {
+        // TODO: Currently hard-coded to a ".html" extension! Need to locate the
+        // ComoponentModel and determine the correct extension from that.
+
+        String path = componentName.replace('.', '/') + ".html";
+        Resource baseResource = new ClasspathResource(_classResolver, path);
+        Resource localized = baseResource.getLocalization(locale);
+
+        _templateResources.put(key, localized);
+
+    }
+
+    /**
+     * Checks to see if any parsed resource has changed. If so, then all internal caches are
+     * cleared, and an invalidation event is fired. This is brute force ... a more targetted
+     * dependency management strategy may come later.
+     */
+    @Synchronized.Write
+    public void checkForUpdates(UpdateEvent event)
+    {
+        if (_tracker.containsChanges())
+        {
+            _tracker.clear();
+            _templateResources.clear();
+            _templates.clear();
+            fireInvalidationEvent();
+        }
+    }
+
+    /** For injection. */
+    public void setClassResolver(ClassResolver classResolver)
+    {
+        _classResolver = classResolver;
+    }
+
+    /** For injection. */
+    public void setParser(TemplateParser parser)
+    {
+        _parser = parser;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoader.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoader.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoader.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoader.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,28 @@
+package org.apache.tapestry.internal.pageload;
+
+import java.util.Locale;
+
+import org.apache.tapestry.internal.structure.Page;
+
+/**
+ * Instantiates a fully loaded, configured instance of a Tapestry page. This is a recursive process,
+ * since part of loading a page is to load the page elements for the page. Further, in some cases,
+ * the full component tree is not identified until after each component's template is loaded. Loaded
+ * pages will be used for many requests (on behalf of many different users) and will be pooled
+ * between requests.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface PageLoader
+{
+    /**
+     * Loads the page in the given locale.
+     * 
+     * @param pageName
+     *            fully qualified class name of the root component of the page
+     * @param locale
+     *            the locale to load the page and its components in
+     * @see Page#getLocale()
+     */
+    Page loadPage(String pageName, Locale locale);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoaderImpl.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoaderImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/pageload/PageLoaderImpl.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,106 @@
+package org.apache.tapestry.internal.pageload;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.ComponentPageElementImpl;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.internal.structure.PageElement;
+import org.apache.tapestry.internal.structure.PageImpl;
+import org.apache.tapestry.internal.transform.ComponentInstantiatorSource;
+import org.apache.tapestry.internal.transform.Instantiator;
+import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.apache.tapestry.util.CollectionFactory;
+
+/**
+ * This implementation is not threadsafe.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class PageLoaderImpl implements PageLoader
+{
+    private ComponentInstantiatorSource _componentInstantiatorSource;
+
+    private Page _page;
+
+    private Locale _locale;
+
+    /**
+     * Stack of elements within the current template, used to determine on which object to invoke
+     * {@link ComponentPageElement#addToBody(PageElement)}. Each new element (component or
+     * otherwise) will push a value onto this stack, each end element will pop a value off the
+     * stack.
+     */
+    private final List<ComponentPageElement> _elementStack = newList();
+
+    private final List<ComponentPageElement> _componentQueue = newList();
+
+    public Page loadPage(String pageName, Locale locale)
+    {
+        try
+        {
+            _locale = locale;
+
+            _page = new PageImpl(pageName, _locale);
+
+            loadRootComponent(pageName);
+
+            workComponentQueue();
+
+            return _page;
+        }
+        finally
+        {
+            _page = null;
+            _locale = null;
+            _elementStack.clear();
+            _componentQueue.clear();
+        }
+    }
+
+    private void loadRootComponent(String componentName)
+    {
+        Instantiator instantiator = _componentInstantiatorSource.findInstantiator(componentName);
+
+        ComponentPageElement rootComponent = new ComponentPageElementImpl(_page, instantiator);
+
+        _page.setRootElement(rootComponent);
+
+        push(_componentQueue, rootComponent);
+    }
+
+    /** Works the component queue, until exausted. */
+    private void workComponentQueue()
+    {
+        
+    }
+    
+    /** For injection. */
+    public void setComponentInstantiatorSource(ComponentInstantiatorSource source)
+    {
+        _componentInstantiatorSource = source;
+    }
+
+    static <T> T peek(List<T> stack)
+    {
+        int size = stack.size();
+
+        return stack.get(size - 1);
+    }
+
+    static <T> void pop(List<T> stack)
+    {
+        int size = stack.size();
+
+        stack.remove(size - 1);
+    }
+
+    static <T> void push(List<T> stack, T element)
+    {
+        stack.add(element);
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/AttributeToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/AttributeToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/AttributeToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/AttributeToken.java Mon Jun  5 09:47:00 2006
@@ -29,7 +29,7 @@
 
     public AttributeToken(String name, String value, Location location)
     {
-        super(location);
+        super(TokenType.ATTRIBUTE, location);
 
         _name = name;
         _value = value;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CDATAToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CDATAToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CDATAToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CDATAToken.java Mon Jun  5 09:47:00 2006
@@ -28,7 +28,7 @@
 
     public CDATAToken(String text, Location location)
     {
-        super(location);
+        super(TokenType.CDATA, location);
 
         _text = text;
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CommentToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CommentToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CommentToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/CommentToken.java Mon Jun  5 09:47:00 2006
@@ -27,7 +27,7 @@
 
     public CommentToken(String comment, Location location)
     {
-        super(location);
+        super(TokenType.COMMENT, location);
 
         _comment = comment;
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/EndElementToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/EndElementToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/EndElementToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/EndElementToken.java Mon Jun  5 09:47:00 2006
@@ -25,6 +25,6 @@
 {
     public EndElementToken(Location location)
     {
-        super(location);
+        super(TokenType.END_ELEMENT, location);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartComponentToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartComponentToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartComponentToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartComponentToken.java Mon Jun  5 09:47:00 2006
@@ -43,7 +43,7 @@
     @SuppressNullCheck
     public StartComponentToken(String id, String type, Location location)
     {
-        super(location);
+        super(TokenType.START_COMPONENT, location);
 
         _id = id;
         _type = type;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartElementToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartElementToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartElementToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/StartElementToken.java Mon Jun  5 09:47:00 2006
@@ -33,7 +33,7 @@
 
     public StartElementToken(String name, Location location)
     {
-        super(location);
+        super(TokenType.START_ELEMENT, location);
 
         _name = name;
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java Mon Jun  5 09:47:00 2006
@@ -25,11 +25,19 @@
  */
 public abstract class TemplateToken implements Locatable
 {
+    private final TokenType _tokenType;
+
     private final Location _location;
 
-    public TemplateToken(Location location)
+    public TemplateToken(TokenType tokenType, Location location)
     {
+        _tokenType = tokenType;
         _location = location;
+    }
+
+    public TokenType getTokenType()
+    {
+        return _tokenType;
     }
 
     public Location getLocation()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TextToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TextToken.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TextToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TextToken.java Mon Jun  5 09:47:00 2006
@@ -22,7 +22,7 @@
 
     public TextToken(String text, Location location)
     {
-        super(location);
+        super(TokenType.TEXT, location);
 
         _text = text;
     }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,12 @@
+package org.apache.tapestry.internal.parser;
+
+/**
+ * Defines the different types of {@link org.apache.tapestry.internal.parser.TemplateToken}s. Each
+ * value maps to a particular subclass of TemplateToken.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public enum TokenType {
+
+    ATTRIBUTE, CDATA, COMMENT, END_ELEMENT, START_COMPONENT, START_ELEMENT, TEXT
+}

Added: 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?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,33 @@
+package org.apache.tapestry.internal.structure;
+
+import org.apache.tapestry.internal.InternalComponentResources;
+
+/**
+ * Extended version of {@link org.apache.tapestry.internal.structure.PageElement} for elements that
+ * are, in fact, components (rather than just static markup).
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface ComponentPageElement extends PageElement, InternalComponentResources
+{
+    /**
+     * Containing component (or null for the root component of a page).
+     */
+    ComponentPageElement getContainer();
+
+    /**
+     * Used during the construction of a page. Adds a page element as part of the template for this
+     * page element. A page element will eventually render by sequentially rendering these elements.
+     * A page elements template is really just the outermost portions of the component's template
+     * ... where a template contains elements that are reall components, those components will
+     * receive portions of the template as their body.
+     */
+
+    void addToTemplate(PageElement element);
+
+    /**
+     * 
+     */
+    void addToBody(PageElement element);
+
+}

Added: 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?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,71 @@
+package org.apache.tapestry.internal.structure;
+
+import java.util.List;
+
+import org.apache.tapestry.internal.transform.Instantiator;
+import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.apache.tapestry.util.CollectionFactory;
+
+/**
+ * Implements {@link org.apache.tapestry.internal.structure.PageElement} and
+ * {@link org.apache.tapestry.internal.InternalComponentResources}, and represents a component
+ * within an overall page. Much of a component page element's behavior is delegated to user code,
+ * via a {@link org.apache.tapestry.runtime.ComponentLifecycle} instance.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class ComponentPageElementImpl implements ComponentPageElement
+{
+    private final Page _page;
+
+    private List<PageElement> _template;
+
+    private List<PageElement> _body;
+
+    private final ComponentPageElement _container;
+
+    private final ComponentLifecycle _component;
+
+    /** Constructor for the root component of a page. */
+    public ComponentPageElementImpl(Page page, Instantiator instantiator)
+    {
+        _page = page;
+        _container = null;
+        _component = instantiator.newInstance(this);
+    }
+
+    /**
+     * Constructor for other components embedded within the root component or at deeper levels of
+     * the hierarchy.
+     */
+
+    public ComponentPageElementImpl(Page page, ComponentPageElement container,
+            Instantiator instantiator)
+    {
+        _page = page;
+        _container = container;
+        _component = instantiator.newInstance(this);
+    }
+
+    public ComponentPageElement getContainer()
+    {
+        return _container;
+    }
+
+    public void addToBody(PageElement element)
+    {
+        if (_body == null)
+            _body = CollectionFactory.newList();
+
+        _body.add(element);
+    }
+
+    public void addToTemplate(PageElement element)
+    {
+        if (_template == null)
+            _template = CollectionFactory.newList();
+
+        _template.add(element);
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/MarkupPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/MarkupPageElement.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/MarkupPageElement.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/MarkupPageElement.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,19 @@
+package org.apache.tapestry.internal.structure;
+
+import org.apache.tapestry.internal.parser.TemplateToken;
+
+/**
+ * Renders static markup based on a {@link org.apache.tapestry.internal.parser.TemplateToken}.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class MarkupPageElement implements PageElement
+{
+    private final TemplateToken _token;
+
+    public MarkupPageElement(TemplateToken token)
+    {
+        _token = token;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java Mon Jun  5 09:47:00 2006
@@ -16,8 +16,6 @@
 
 import java.util.Locale;
 
-import org.apache.tapestry.runtime.ComponentLifecycle;
-
 /**
  * Represents a unique page within the application.
  * <p>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageElement.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageElement.java Mon Jun  5 09:47:00 2006
@@ -23,7 +23,4 @@
  */
 public interface PageElement
 {
-    Page getPage();
-
-    PageElement getContainer();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSource.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSource.java Mon Jun  5 09:47:00 2006
@@ -34,12 +34,15 @@
 
     /**
      * Given the name of a component class, provides an instantiator for that component.
+     * Instantiators are cached, so repeated calls to this method with the same class name will
+     * return the same instance; however, callers should also be aware that the instantiators may
+     * lose validity after an invalidation (caused by changes to external Java class files).
      * 
      * @param classname
      *            FQCN to find (and perhaps transform and load)
      * @return an object which can instantiate an instance of the component
      */
-    Instantiator createInstantiator(String classname);
+    Instantiator findInstantiator(String classname);
 
     /**
      * Adds a controlled package. Only classes within controlled packages are subject to

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java Mon Jun  5 09:47:00 2006
@@ -16,10 +16,8 @@
 
 import static org.apache.tapestry.util.CollectionFactory.newMap;
 import static org.apache.tapestry.util.CollectionFactory.newSet;
-import static org.apache.tapestry.util.CollectionFactory.newThreadSafeList;
 
 import java.net.URL;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -34,10 +32,9 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.hivemind.ClassResolver;
-import org.apache.tapestry.events.InvalidationEvent;
-import org.apache.tapestry.events.InvalidationListener;
 import org.apache.tapestry.events.UpdateEvent;
 import org.apache.tapestry.events.UpdateListener;
+import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry.internal.util.URLChangeTracker;
 import org.apache.tapestry.util.Defense;
 
@@ -47,8 +44,8 @@
  * 
  * @author Howard M. Lewis Ship
  */
-public final class ComponentInstantiatorSourceImpl implements Translator,
-        ComponentInstantiatorSource, UpdateListener
+public final class ComponentInstantiatorSourceImpl extends InvalidationEventHubImpl implements
+        Translator, ComponentInstantiatorSource, UpdateListener
 {
     private final Set<String> _controlledPackageNames = newSet();
 
@@ -67,8 +64,6 @@
     /** Map from class name to Instantiator. */
     private final Map<String, Instantiator> _instantiatorMap = newMap();
 
-    private final List<InvalidationListener> _listeners = newThreadSafeList();
-
     /** @return the class loader used when loading enhanced/modified classes */
     public ClassLoader getClassLoader()
     {
@@ -120,12 +115,7 @@
 
         initializeService();
 
-        InvalidationEvent invalidationEvent = new InvalidationEvent(this);
-
-        for (InvalidationListener listener : _listeners)
-        {
-            listener.objectWasInvalidated(invalidationEvent);
-        }
+        fireInvalidationEvent();
     }
 
     public void initializeService()
@@ -201,7 +191,7 @@
     {
     }
 
-    public synchronized Instantiator createInstantiator(String classname)
+    public synchronized Instantiator findInstantiator(String classname)
     {
         Instantiator result = _instantiatorMap.get(classname);
 
@@ -279,16 +269,6 @@
     public void setLog(Log log)
     {
         _log = log;
-    }
-
-    public void addInvalidationListener(InvalidationListener listener)
-    {
-        _listeners.add(listener);
-    }
-
-    public void removeInvalidationListener(InvalidationListener listener)
-    {
-        _listeners.remove(listener);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentLifecycle.java Mon Jun  5 09:47:00 2006
@@ -14,12 +14,11 @@
 
 package org.apache.tapestry.runtime;
 
-
 /**
- * Interface that allows a component to define behavior as the page is loaded. This interface is
- * part of the public API for Tapestry, but is <em>not</em> expected to be directly implemented by
- * component classes; it should only be implemented as part of the component class transformation
- * process. 
+ * Interface that defining the lifecycle of a component, within a page, allowing for callbacks into
+ * the component for many different events. This interface is part of the public API for Tapestry,
+ * but is <em>not</em> expected to be directly implemented by component classes; it should only be
+ * implemented as part of the component class transformation process.
  * 
  * @author Howard M. Lewis Ship
  */

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java Mon Jun  5 09:47:00 2006
@@ -14,12 +14,24 @@
 
 package org.apache.tapestry.test;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.lang.annotation.Annotation;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
 import java.util.List;
+import java.util.UUID;
 
 import org.apache.commons.logging.Log;
+import org.apache.hivemind.Resource;
+import org.apache.hivemind.util.ClasspathResource;
 import org.apache.tapestry.internal.InternalComponentResources;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.internal.parser.ComponentTemplate;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.transform.ClassTransformation;
 
@@ -87,6 +99,55 @@
     {
         transformation.findUnclaimedFields();
         setReturnValue(fieldNames);
+    }
+
+
+
+    /** Writes a change to a file. */
+    protected final void touch(File f) throws FileNotFoundException, IOException
+    {
+        OutputStream o = new FileOutputStream(f);
+        o.write(0);
+        o.close();
+    }
+
+    /**
+     * Creates a new class loader, whose parent is the thread's context class loader, but adds a
+     * single classpath root from the filesystem.
+     * 
+     * @see #createClasspathRoot()
+     */
+    protected final URLClassLoader newLoaderWithClasspathRoot(File rootDir) throws MalformedURLException
+    {
+        String urlPath = rootDir.toURL().toString();
+        // URLs for folders must end with a slash to make URLClassLoader happy.
+        URL url = new URL(urlPath + "/");
+    
+        return new URLClassLoader(new URL[]
+        { url }, Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * Creates a new temporary directory which can act as a classpath root.
+     * 
+     * @see #newLoaderWithClasspathRoot(File)
+     */
+    protected final File createClasspathRoot()
+    {
+        String temp = System.getProperty("java.io.tmpdir");
+        String rootDirPath = temp + "/" + UUID.randomUUID().toString();
+    
+        return new File(rootDirPath);
+    }
+
+    protected final Resource newResource()
+    {
+        return newMock(Resource.class);
+    }
+
+    protected final ComponentTemplate newComponentTemplate()
+    {
+        return newMock(ComponentTemplate.class);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml Mon Jun  5 09:47:00 2006
@@ -24,6 +24,7 @@
       <package name="org.apache.tapestry.internal.aspects"/>
       <package name="org.apache.tapestry.internal.util"/>
       <package name="org.apache.tapestry.internal.parser"/>
+      <package name="org.apache.tapestry.internal.pageload"/>
       <package name="org.apache.tapestry.util"/>
     </packages>
   </test>

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImplTest.java?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/pageload/ComponentTemplateSourceImplTest.java Mon Jun  5 09:47:00 2006
@@ -0,0 +1,197 @@
+package org.apache.tapestry.internal.pageload;
+
+import static org.testng.Assert.assertSame;
+
+import java.io.File;
+import java.net.URLClassLoader;
+import java.util.Locale;
+
+import org.apache.hivemind.ClassResolver;
+import org.apache.hivemind.Resource;
+import org.apache.hivemind.impl.DefaultClassResolver;
+import org.apache.hivemind.util.ClasspathResource;
+import org.apache.tapestry.events.InvalidationEvent;
+import org.apache.tapestry.events.InvalidationListener;
+import org.apache.tapestry.events.UpdateEvent;
+import org.apache.tapestry.internal.parser.ComponentTemplate;
+import org.apache.tapestry.internal.parser.TemplateParser;
+import org.apache.tapestry.test.BaseTestCase;
+import org.easymock.EasyMock;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class ComponentTemplateSourceImplTest extends BaseTestCase
+{
+    private static final String PACKAGE = "org.apache.tapestry.internal.pageload";
+
+    static public final String PATH = "org/apache/tapestry/internal/pageload";
+
+    public ClassResolver _resolver = new DefaultClassResolver();
+
+    @Test
+    public void caching()
+    {
+        Resource r = newResource("Fred.html");
+
+        TemplateParser parser = newTemplateParser();
+        ComponentTemplate template = newComponentTemplate();
+
+        trainParseTemplate(parser, r, template);
+
+        replay();
+
+        ComponentTemplateSourceImpl source = new ComponentTemplateSourceImpl();
+        source.setParser(parser);
+        source.setClassResolver(_resolver);
+
+        String name = PACKAGE + ".Fred";
+        assertSame(source.getTemplate(name, Locale.ENGLISH), template);
+
+        // A second pass will test the caching (the
+        // parser is not invoked).
+
+        assertSame(source.getTemplate(name, Locale.ENGLISH), template);
+
+        verify();
+    }
+
+    private void trainParseTemplate(TemplateParser parser, Resource resource,
+            ComponentTemplate template)
+    {
+        parser.parseTemplate(resource);
+        setReturnValue(template);
+    }
+
+    /** Tests resource invalidation. */
+    @Test
+    public void invalidation() throws Exception
+    {
+        File rootDir = createClasspathRoot();
+        URLClassLoader loader = newLoaderWithClasspathRoot(rootDir);
+
+        File packageDir = new File(rootDir, "baz");
+        packageDir.mkdirs();
+
+        File f = new File(packageDir, "Biff.html");
+
+        f.createNewFile();
+
+        // A special resolver that knows about our special class path.
+
+        ClassResolver resolver = new DefaultClassResolver(loader);
+
+        Resource localized = new ClasspathResource(resolver, "baz/Biff.html");
+
+        TemplateParser parser = newTemplateParser();
+        ComponentTemplate template = newComponentTemplate();
+        InvalidationListener listener = newMock(InvalidationListener.class);
+
+        trainParseTemplate(parser, localized, template);
+
+        replay();
+
+        ComponentTemplateSourceImpl source = new ComponentTemplateSourceImpl();
+        source.setParser(parser);
+        source.setClassResolver(resolver);
+        source.addInvalidationListener(listener);
+
+        String name = "baz.Biff";
+
+        assertSame(source.getTemplate(name, Locale.ENGLISH), template);
+
+        // Check for updates (which won't be found).
+        source.checkForUpdates(new UpdateEvent(this));
+
+        // A second pass will test the caching (the
+        // parser is not invoked).
+
+        assertSame(source.getTemplate(name, Locale.ENGLISH), template);
+
+        verify();
+
+        // Now, change the file and process an UpdateEvent.
+
+        touch(f);
+
+        listener.objectWasInvalidated(EasyMock.isA(InvalidationEvent.class));
+
+        replay();
+
+        // Check for updates (which will be found).
+        source.checkForUpdates(new UpdateEvent(this));
+
+        verify();
+
+        // Check that the cache really is cleared.
+
+        trainParseTemplate(parser, localized, template);
+
+        replay();
+
+        assertSame(source.getTemplate(name, Locale.ENGLISH), template);
+
+        verify();
+    }
+
+    protected final TemplateParser newTemplateParser()
+    {
+        return newMock(TemplateParser.class);
+    }
+
+    /** Checks that localization to the same resource works (w/ caching). */
+    @Test
+    public void localizationToSame()
+    {
+        Resource r = newResource("Fred.html");
+
+        TemplateParser parser = newTemplateParser();
+        ComponentTemplate template = newComponentTemplate();
+
+        trainParseTemplate(parser, r, template);
+
+        replay();
+
+        ComponentTemplateSourceImpl source = new ComponentTemplateSourceImpl();
+        source.setParser(parser);
+        source.setClassResolver(_resolver);
+
+        String name = PACKAGE + ".Fred";
+        assertSame(source.getTemplate(name, Locale.ENGLISH), template);
+
+        // A second pass finds the same resource, but using a different
+        // path/locale combination.
+
+        assertSame(source.getTemplate(name, Locale.FRENCH), template);
+
+        // A third pass should further demonstrate the caching.
+
+        assertSame(source.getTemplate(name, Locale.FRENCH), template);
+
+        verify();
+    }
+
+    @Test
+    public void notFound()
+    {
+        TemplateParser parser = newTemplateParser();
+
+        replay();
+
+        ComponentTemplateSourceImpl source = new ComponentTemplateSourceImpl();
+        source.setParser(parser);
+        source.setClassResolver(_resolver);
+
+        String name = PACKAGE + ".Barney";
+        Assert.assertNull(source.getTemplate(name, Locale.ENGLISH));
+
+        verify();
+    }
+
+    private Resource newResource(String name)
+    {
+        return new ClasspathResource(_resolver, PATH + "/" + name);
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/parser/TemplateParserImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/parser/TemplateParserImplTest.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/parser/TemplateParserImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/parser/TemplateParserImplTest.java Mon Jun  5 09:47:00 2006
@@ -234,8 +234,7 @@
         AttributeToken t1 = get(tokens, 3);
 
         // TODO: Not sure what order the attributes appear in. Order in the XML? Sorted
-        // alphbetically?
-        // Random 'cause they're hashed?
+        // alphbetically? Random 'cause they're hashed?
 
         assertEquals(t1.getName(), "cherry");
         assertEquals(t1.getValue(), "bomb");

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java?rev=411855&r1=411854&r2=411855&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java Mon Jun  5 09:47:00 2006
@@ -208,7 +208,7 @@
         // Can't wait for the HiveMind code base to start using some generics for this kind of
         // thing.
 
-        Instantiator inst = _source.createInstantiator(classname);
+        Instantiator inst = _source.findInstantiator(classname);
 
         ComponentLifecycle target = inst.newInstance(resources);
 

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/pageload/Fred.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/pageload/Fred.html?rev=411855&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/pageload/Fred.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/pageload/Fred.html Mon Jun  5 09:47:00 2006
@@ -0,0 +1 @@
+<html> Used by ComponentTemplateSourceImplTest </html>