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 2008/12/22 20:50:28 UTC

svn commit: r728754 [2/3] - in /tapestry/tapestry5/trunk: ./ src/site/apt/ src/site/apt/guide/ tapestry-core/ tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ tapestry-core/src/main/java/org/apache/tapestry5/dom/ tapestry-core/src/m...

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Mon Dec 22 11:50:26 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.*;
+import org.apache.tapestry5.annotations.*;
 import org.apache.tapestry5.dom.Element;
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
@@ -31,21 +32,20 @@
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.model.ParameterModel;
+import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.*;
 import org.slf4j.Logger;
 
 import java.util.*;
 
 /**
- * Implements {@link org.apache.tapestry5.internal.structure.PageElement} 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.tapestry5.runtime.Component} instance.
+ * Implements {@link RenderCommand} 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.tapestry5.runtime.Component} instance.
  * <p/>
  * Once instantiated, a ComponentPageElement should be registered as a {@linkplain
  * org.apache.tapestry5.internal.structure.Page#addLifecycleListener(org.apache.tapestry5.runtime.PageLifecycleListener)
  * lifecycle listener}. This could be done inside the constructors, but that tends to complicate unit tests, so its done
  * by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}.
- * <p/>
  */
 public class ComponentPageElementImpl extends BaseLocatable implements ComponentPageElement, PageLifecycleListener
 {
@@ -67,16 +67,6 @@
 
     private static final Block PLACEHOLDER_BLOCK = new PlaceholderBlock();
 
-    /**
-     * @see #render(org.apache.tapestry5.MarkupWriter, org.apache.tapestry5.runtime.RenderQueue)
-     */
-    private static final RenderCommand POP_COMPONENT_ID = new RenderCommand()
-    {
-        public void render(MarkupWriter writer, RenderQueue queue)
-        {
-            queue.endComponent();
-        }
-    };
 
     private static final ComponentCallback CONTAINING_PAGE_DID_ATTACH = new LifecycleNotificationComponentCallback()
     {
@@ -113,7 +103,7 @@
     // For the moment, every component will have a template, even if it consists of
     // just a page element to queue up a BeforeRenderBody phase.
 
-    private static void pushElements(RenderQueue queue, List<PageElement> list)
+    private static void pushElements(RenderQueue queue, List<RenderCommand> list)
     {
         int count = size(list);
         for (int i = count - 1; i >= 0; i--)
@@ -125,316 +115,339 @@
         return list == null ? 0 : list.size();
     }
 
-    private static class RenderPhaseEventHandler implements ComponentEventCallback
+    private abstract class AbstractPhase extends AbstractComponentCallback implements RenderCommand
     {
-        private boolean result = true;
+        private final String name;
 
-        private List<RenderCommand> commands;
+        private MarkupWriter writer;
 
-        boolean getResult()
+        public AbstractPhase(String name)
         {
-            return result;
+            super(sharedEvent);
+
+            this.name = name;
         }
 
-        public boolean handleResult(Object result)
+        @Override
+        public String toString()
         {
-            if (result instanceof Boolean)
-            {
-                this.result = (Boolean) result;
-                return true; // abort other handler methods
-            }
-
-            if (result instanceof RenderCommand)
-            {
-                RenderCommand command = (RenderCommand) result;
-
-                add(command);
-
-                return false; // do not abort!
-            }
+            return phaseToString(name);
+        }
 
-            if (result instanceof Renderable)
-            {
-                final Renderable renderable = (Renderable) result;
+        void reset(RenderQueue queue)
+        {
+            sharedEventHandler.queueCommands(queue);
 
-                RenderCommand wrapper = new RenderCommand()
-                {
-                    public void render(MarkupWriter writer, RenderQueue queue)
-                    {
-                        renderable.render(writer);
-                    }
-                };
+            sharedEventHandler.reset();
 
-                add(wrapper);
+            sharedEvent.reset();
 
-                return false;
-            }
-
-            throw new RuntimeException(StructureMessages.wrongPhaseResultType(Boolean.class));
+            writer = null;
         }
 
-        private void add(RenderCommand command)
+        void callback(boolean reverse, MarkupWriter writer)
         {
-            if (commands == null) commands = CollectionFactory.newList();
+            this.writer = writer;
 
-            commands.add(command);
+            invoke(reverse, this);
         }
 
-        public void queueCommands(RenderQueue queue)
+        public void run(Component component)
         {
-            if (commands == null) return;
+            invokeComponent(component, writer, sharedEvent);
+        }
 
-            for (RenderCommand command : commands)
-                queue.push(command);
+        protected boolean getResult()
+        {
+            return sharedEventHandler.getResult();
         }
+
+        protected abstract void invokeComponent(Component component, MarkupWriter writer, Event event);
     }
 
-    private final RenderCommand afterRender = new RenderCommand()
+    private class SetupRenderPhase extends AbstractPhase
     {
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        public SetupRenderPhase()
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            super("SetupRender");
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.afterRender(writer, event);
-                }
-            };
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.setupRender(writer, event);
+        }
 
-            invoke(true, callback);
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(false, writer);
 
-            if (!handler.getResult()) queue.push(beginRender);
+            push(queue, getResult(), beginRenderPhase, cleanupRenderPhase);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
+    private class BeginRenderPhase extends AbstractPhase
+    {
+        private BeginRenderPhase()
         {
-            return phaseToString("AfterRender");
+            super("BeginRender");
         }
-    };
 
-    private final RenderCommand afterRenderBody = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            component.beginRender(writer, event);
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.afterRenderBody(writer, event);
-                }
-            };
+        public void render(final MarkupWriter writer, final RenderQueue queue)
+        {
+            callback(false, writer);
 
-            invoke(true, callback);
+            push(queue, afterRenderPhase);
+            push(queue, getResult(), beforeRenderTemplatePhase, null);
 
-            if (!handler.getResult()) queue.push(beforeRenderBody);
+            reset(queue);
+        }
+    }
 
-            handler.queueCommands(queue);
+    /**
+     * Replaces {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl.BeginRenderPhase} when there a
+     * handler for AfterRender but not BeginRender.
+     */
+    private class OptimizedBeginRenderPhase implements RenderCommand
+    {
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            push(queue, afterRenderPhase);
+            push(queue, beforeRenderTemplatePhase);
         }
 
         @Override
         public String toString()
         {
-            return phaseToString("AfterRenderBody");
+            return phaseToString("OptimizedBeginRenderPhase");
         }
-    };
+    }
 
-    private final RenderCommand afterRenderTemplate = new RenderCommand()
+    /**
+     * Reponsible for rendering the component's template.  Even a component that doesn't have a template goes through
+     * this phase, as a synthetic template (used to trigger the rendering of the component's body) will be supplied.
+     */
+    private class BeforeRenderTemplatePhase extends AbstractPhase
     {
+        private BeforeRenderTemplatePhase()
+        {
+            super("BeforeRenderTemplate");
+        }
+
+
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.beforeRenderTemplate(writer, event);
+        }
+
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            callback(false, writer);
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.afterRenderTemplate(writer, event);
-                }
-            };
+            push(queue, afterRenderTemplatePhase);
 
-            invoke(true, callback);
+            if (getResult())
+                pushElements(queue, template);
 
-            if (!handler.getResult()) queue.push(beforeRenderTemplate);
+            reset(queue);
+        }
+    }
 
-            handler.queueCommands(queue);
+    /**
+     * Alternative version of BeforeRenderTemplatePhase used when the BeforeRenderTemplate render phase is not handled.
+     */
+    private class RenderTemplatePhase implements RenderCommand
+    {
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            push(queue, afterRenderTemplatePhase);
+
+            pushElements(queue, template);
         }
 
         @Override
         public String toString()
         {
-            return phaseToString("AfterRenderTemplate");
+            return phaseToString("RenderTemplate");
         }
-    };
+    }
 
-    private final RenderCommand beforeRenderBody = new RenderCommand()
+    private class BeforeRenderBodyPhase extends AbstractPhase
     {
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        private BeforeRenderBodyPhase()
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            super("BeforeRenderBody");
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.beforeRenderBody(writer, event);
-                }
-            };
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.beforeRenderBody(writer, event);
+        }
 
-            invoke(false, callback);
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(false, writer);
 
-            queue.push(afterRenderBody);
+            push(queue, afterRenderBodyPhase);
 
-            if (handler.getResult() && bodyBlock != null) queue.push(bodyBlock);
+            if (getResult() && bodyBlock != null)
+                queue.push(bodyBlock);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
-        {
-            return phaseToString("BeforeRenderBody");
-        }
-    };
 
-    private final RenderCommand beforeRenderTemplate = new RenderCommand()
+    private class AfterRenderBodyPhase extends AbstractPhase
     {
-        public void render(final MarkupWriter writer, final RenderQueue queue)
+
+        private AfterRenderBodyPhase()
         {
-            final RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            super("AfterRenderBody");
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.beforeRenderTemplate(writer, event);
-                }
-            };
 
-            invoke(false, callback);
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.afterRenderBody(writer, event);
+        }
 
-            queue.push(afterRenderTemplate);
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(true, writer);
 
-            if (handler.getResult()) pushElements(queue, template);
+            push(queue, getResult(), null, beforeRenderBodyPhase);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
+    private class AfterRenderTemplatePhase extends AbstractPhase
+    {
+        private AfterRenderTemplatePhase()
         {
-            return phaseToString("BeforeRenderTemplate");
+            super("AfterRenderTemplate");
         }
-    };
 
-    private final RenderCommand beginRender = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, final RenderQueue queue)
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
-
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.beginRender(writer, event);
-                }
-            };
-
-            invoke(false, callback);
+            component.afterRenderTemplate(writer, event);
+        }
 
-            queue.push(afterRender);
+        public void render(final MarkupWriter writer, final RenderQueue queue)
+        {
+            callback(true, writer);
 
-            // If the component has no template whatsoever, then a
-            // renderBody element is added as the lone element of the component's template.
-            // So every component will have a non-empty template.
+            push(queue, getResult(), null, beforeRenderTemplatePhase);
 
-            if (handler.getResult()) queue.push(beforeRenderTemplate);
+            reset(queue);
+        }
+    }
 
-            handler.queueCommands(queue);
+    private class AfterRenderPhase extends AbstractPhase
+    {
+        private AfterRenderPhase()
+        {
+            super("AfterRender");
         }
 
-        @Override
-        public String toString()
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
         {
-            return phaseToString("BeginRender");
+            component.afterRender(writer, event);
         }
-    };
 
-    private Map<String, Block> blocks;
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(true, writer);
 
-    private BlockImpl bodyBlock;
+            push(queue, getResult(), cleanupRenderPhase, beginRenderPhase);
 
-    private Map<String, ComponentPageElement> children;
+            reset(queue);
+        }
+    }
 
-    private final String elementName;
 
     private final ComponentPageElementResources elementResources;
 
-    private final Logger logger;
-
-    private final RenderCommand cleanupRender = new RenderCommand()
+    private class CleanupRenderPhase extends AbstractPhase
     {
+        private CleanupRenderPhase()
+        {
+            super("CleanupRender");
+        }
+
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.cleanupRender(writer, event);
+        }
+
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            callback(true, writer);
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.cleanupRender(writer, event);
-                }
-            };
+            push(queue, getResult(), null, setupRenderPhase);
 
-            invoke(true, callback);
+            reset(queue);
+        }
+    }
 
-            if (handler.getResult())
-            {
-                rendering = false;
+    private class PostRenderCleanupPhase implements RenderCommand
+    {
+        /**
+         * Used to detect mismatches calls to {@link MarkupWriter#element(String, Object[])} } and {@link
+         * org.apache.tapestry5.MarkupWriter#end()}.  The expectation is that any element(s) begun by this component
+         * during rendering will be balanced by end() calls, resulting in the current element reverting to its initial
+         * value.
+         */
+        private final Element expectedElementAtCompletion;
 
-                Element current = writer.getElement();
+        PostRenderCleanupPhase(Element expectedElementAtCompletion)
+        {
+            this.expectedElementAtCompletion = expectedElementAtCompletion;
+        }
+
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            rendering = false;
 
-                if (current != elementAtSetup)
-                    throw new TapestryException(StructureMessages.unbalancedElements(completeId), getLocation(), null);
+            Element current = writer.getElement();
 
-                elementAtSetup = null;
+            if (current != expectedElementAtCompletion)
+                throw new TapestryException(StructureMessages.unbalancedElements(completeId), getLocation(), null);
 
-                invoke(false, POST_RENDER_CLEANUP);
+            invoke(false, POST_RENDER_CLEANUP);
 
-                // NOW and only now the component is done rendering and fully cleaned up. Decrement
-                // the page's dirty count. If the entire render goes well, then the page will be
-                // clean and can be stored into the pool for later reuse.
+            queue.endComponent();
 
-                page.decrementDirtyCount();
-            }
-            else
-            {
-                queue.push(setupRender);
-            }
+            // Now and only now the component is done rendering and fully cleaned up. Decrement
+            // the page's dirty count. If the entire render goes well, then the page will be
+            // clean and can be stored into the pool for later reuse.
 
-            handler.queueCommands(queue);
+            page.decrementDirtyCount();
         }
 
         @Override
         public String toString()
         {
-            return phaseToString("CleanupRender");
+            return phaseToString("PostRenderCleanup");
         }
-    };
+    }
+
+    private Map<String, Block> blocks;
+
+    private BlockImpl bodyBlock;
+
+    private Map<String, ComponentPageElement> children;
+
+    private final String elementName;
+
+    private final Logger eventLogger;
 
     private final String completeId;
 
@@ -468,65 +481,21 @@
 
     private boolean rendering;
 
-    /**
-     * Used to detect mismatches calls to {@link MarkupWriter#element(String, Object[])} } and {@link
-     * org.apache.tapestry5.MarkupWriter#end()}.  The expectation is that any element(s) begun by this component during
-     * rendering will be balanced by end() calls, resulting in the current element reverting to its initial value.
-     */
-    private Element elementAtSetup;
-
-    private final RenderCommand setupRender = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, RenderQueue queue)
-        {
-            // TODO: Check for recursive rendering.
-
-            rendering = true;
-
-            elementAtSetup = writer.getElement();
-
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
-
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.setupRender(writer, event);
-                }
-            };
-
-            invoke(false, callback);
-
-            queue.push(cleanupRender);
-
-            if (handler.getResult()) queue.push(beginRender);
-
-            handler.queueCommands(queue);
-        }
-
-        @Override
-        public String toString()
-        {
-            return phaseToString("SetupRender");
-        }
-    };
 
     // We know that, at the very least, there will be an element to force the component to render
     // its body, so there's no reason to wait to initialize the list.
 
-    private final List<PageElement> template = CollectionFactory.newList();
+    private final List<RenderCommand> template = CollectionFactory.newList();
 
+    private boolean renderPhasesInitalized;
 
-    public ComponentPageElement newChild(String id, String elementName, Instantiator instantiator, Location location)
-    {
-        ComponentPageElementImpl child = new ComponentPageElementImpl(page, this, id, elementName, instantiator,
-                                                                      location, elementResources);
+    private RenderCommand setupRenderPhase, beginRenderPhase, beforeRenderTemplatePhase, beforeRenderBodyPhase,
+            afterRenderBodyPhase, afterRenderTemplatePhase, afterRenderPhase, cleanupRenderPhase;
 
-        addEmbeddedElement(child);
 
-        return child;
-    }
+    private final RenderPhaseEventHandler sharedEventHandler = new RenderPhaseEventHandler();
+
+    private final EventImpl sharedEvent;
 
     /**
      * Constructor for other components embedded within the root component or at deeper levels of the hierarchy.
@@ -593,7 +562,10 @@
 
         coreComponent = coreResources.getComponent();
 
-        logger = coreResources.getLogger();
+        Logger logger = coreResources.getLogger();
+        eventLogger = elementResources.getEventLogger(logger);
+
+        sharedEvent = new EventImpl(sharedEventHandler, eventLogger);
     }
 
     /**
@@ -605,6 +577,82 @@
         this(page, null, null, null, instantiator, null, elementResources);
     }
 
+    private void initializeRenderPhases()
+    {
+        setupRenderPhase = new SetupRenderPhase();
+        beginRenderPhase = new BeginRenderPhase();
+        beforeRenderTemplatePhase = new BeforeRenderTemplatePhase();
+        beforeRenderBodyPhase = new BeforeRenderBodyPhase();
+        afterRenderBodyPhase = new AfterRenderBodyPhase();
+        afterRenderTemplatePhase = new AfterRenderTemplatePhase();
+        afterRenderPhase = new AfterRenderPhase();
+        cleanupRenderPhase = new CleanupRenderPhase();
+
+
+        // Now the optimization, where we remove, replace and collapse unused phases. We use
+        // the component models to determine which phases have handler methods for the
+        // render phases.
+
+        Set<Class> handled = coreResources.getComponentModel().getHandledRenderPhases();
+
+        if (mixinIdToComponentResources != null)
+        {
+            for (ComponentResources r : mixinIdToComponentResources.values())
+                handled.addAll(r.getComponentModel().getHandledRenderPhases());
+        }
+
+        if (!handled.contains(CleanupRender.class)) cleanupRenderPhase = null;
+
+        // Now, work back to front.
+
+        if (!handled.contains(AfterRender.class))
+            afterRenderPhase = cleanupRenderPhase;
+
+        if (!handled.contains(AfterRenderTemplate.class))
+            afterRenderTemplatePhase = null;
+
+        if (!handled.contains(AfterRenderBody.class))
+            afterRenderBodyPhase = null;
+
+        if (!handled.contains(BeforeRenderTemplate.class))
+            beforeRenderTemplatePhase = new RenderTemplatePhase();
+
+        if (!handled.contains(BeginRender.class))
+        {
+            RenderCommand replacement = handled.contains(AfterRender.class)
+                                        ? new OptimizedBeginRenderPhase()
+                                        : beforeRenderTemplatePhase;
+
+            beginRenderPhase = replacement;
+        }
+
+        if (!handled.contains(SetupRender.class))
+            setupRenderPhase = beginRenderPhase;
+
+        renderPhasesInitalized = true;
+    }
+
+    public ComponentPageElement newChild(String id, String elementName, Instantiator instantiator, Location location)
+    {
+        ComponentPageElementImpl child = new ComponentPageElementImpl(page, this, id, elementName, instantiator,
+                                                                      location, elementResources);
+
+        addEmbeddedElement(child);
+
+        return child;
+    }
+
+    void push(RenderQueue queue, boolean forward, RenderCommand forwardPhase, RenderCommand backwardPhase)
+    {
+        push(queue, forward ? forwardPhase : backwardPhase);
+    }
+
+    void push(RenderQueue queue, RenderCommand nextPhase)
+    {
+        if (nextPhase != null)
+            queue.push(nextPhase);
+    }
+
     void addEmbeddedElement(ComponentPageElement child)
     {
         if (children == null) children = CollectionFactory.newCaseInsensitiveMap();
@@ -612,6 +660,7 @@
         String childId = child.getId();
 
         ComponentPageElement existing = children.get(childId);
+
         if (existing != null)
             throw new TapestryException(StructureMessages.duplicateChildComponent(this, childId), child, null);
 
@@ -698,14 +747,14 @@
         if (informalParameterResources != null) informalParameterResources.bindParameter(parameterName, binding);
     }
 
-    public void addToBody(PageElement element)
+    public void addToBody(RenderCommand element)
     {
         if (bodyBlock == null) bodyBlock = new BlockImpl(getLocation(), "Body of " + getCompleteId());
 
         bodyBlock.addToBody(element);
     }
 
-    public void addToTemplate(PageElement element)
+    public void addToTemplate(RenderCommand element)
     {
         template.add(element);
     }
@@ -789,7 +838,8 @@
     {
         // If no body, then no beforeRenderBody or afterRenderBody
 
-        if (bodyBlock != null) queue.push(beforeRenderBody);
+        if (bodyBlock != null)
+            push(queue, beforeRenderBodyPhase);
     }
 
     public String getCompleteId()
@@ -960,6 +1010,12 @@
      */
     public final void render(MarkupWriter writer, RenderQueue queue)
     {
+        // We assume that by the time we start to render, the structure (i.e., mixins) is nailed down.
+        // We could add a lock, but that seems wasteful.
+
+        if (!renderPhasesInitalized)
+            initializeRenderPhases();
+
         // TODO: An error if the _render flag is already set (recursive rendering not
         // allowed or advisable).
 
@@ -967,14 +1023,15 @@
 
         page.incrementDirtyCount();
 
-        queue.startComponent(coreResources);
+        // TODO: Check for recursive rendering.
+
+        rendering = true;
 
-        // POP_COMPONENT_ID will remove the component we just started.
+        queue.startComponent(coreResources);
 
-        queue.push(POP_COMPONENT_ID);
+        queue.push(new PostRenderCleanupPhase(writer.getElement()));
 
-        // This is the start of the real state machine for the component.
-        queue.push(setupRender);
+        push(queue, setupRenderPhase);
     }
 
     @Override
@@ -1217,7 +1274,7 @@
 
     public Logger getEventLogger()
     {
-        return elementResources.getEventLogger(logger);
+        return eventLogger;
     }
 
     public Link createEventLink(String eventType, Object... context)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/DTDPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/DTDPageElement.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/DTDPageElement.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/DTDPageElement.java Mon Dec 22 11:50:26 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,17 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/*
- * Created on Mar 15, 2007
- * 
- * 
- */
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.runtime.RenderCommand;
 import org.apache.tapestry5.runtime.RenderQueue;
 
-public class DTDPageElement implements PageElement
+public class DTDPageElement implements RenderCommand
 {
     private final String name;
 
@@ -47,5 +43,4 @@
     {
         return String.format("DTD[name=%s; publicId=%s; systemId=%s]", name, publicId, systemId);
     }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ExpansionPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ExpansionPageElement.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ExpansionPageElement.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ExpansionPageElement.java Mon Dec 22 11:50:26 2008
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,12 +17,10 @@
 import org.apache.tapestry5.Binding;
 import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.runtime.RenderCommand;
 import org.apache.tapestry5.runtime.RenderQueue;
 
-/**
- *
- */
-public class ExpansionPageElement implements PageElement
+public class ExpansionPageElement implements RenderCommand
 {
     private final Binding binding;
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java Mon Dec 22 11:50:26 2008
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry5.*;
 import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.ParameterAccess;
 import org.apache.tapestry5.internal.services.Instantiator;
 import org.apache.tapestry5.ioc.AnnotationProvider;
 import org.apache.tapestry5.ioc.Location;
@@ -59,9 +60,12 @@
 
     private final ComponentPageElementResources elementResources;
 
-    // Case insensitive
+    // Case insensitive map from parameter name to binding
     private Map<String, Binding> bindings;
 
+    // Case insensitive map from parameter name to ParameterAccess
+    private Map<String, ParameterAccess> access;
+
     private Messages messages;
 
     // Case insensitive
@@ -69,6 +73,48 @@
 
     private static final Object[] EMPTY = new Object[0];
 
+    private boolean informalsComputed;
+
+    /**
+     * We keep a linked list of informal parameters, which saves us the expense of determining which bindings are formal
+     * and which are informal. Each Informal points to the next.
+     */
+    private class Informal
+    {
+        private final String name;
+
+        private final Binding binding;
+
+        final Informal next;
+
+        private Informal(String name, Binding binding, Informal next)
+        {
+            this.name = name;
+            this.binding = binding;
+            this.next = next;
+        }
+
+        void write(MarkupWriter writer)
+        {
+            Object value = binding.get();
+
+            if (value == null) return;
+
+            if (value instanceof Block) return;
+
+            // If it's already a String, don't use the TypeCoercer (renderInformalParameters is
+            // a CPU hotspot, as is TypeCoercer.coerce).
+
+            String valueString = value instanceof String
+                                 ? (String) value
+                                 : elementResources.coerce(value, String.class);
+
+            writer.attributes(name, valueString);
+        }
+    }
+
+    private Informal firstInformal;
+
     public InternalComponentResourcesImpl(Page page, ComponentPageElement element,
                                           ComponentResources containerResources,
                                           ComponentPageElementResources elementResources,
@@ -164,9 +210,7 @@
 
     public <T> T getInformalParameter(String name, Class<T> type)
     {
-        if (getBinding(name) != null) return readParameter(name, type);
-
-        return null;
+        return getParameterAccess(name).read(type);
     }
 
     public Block getBody()
@@ -196,11 +240,7 @@
 
     public <T extends Annotation> T getParameterAnnotation(String parameterName, Class<T> annotationType)
     {
-        Binding b = getBinding(parameterName);
-
-        if (b == null) return null;
-
-        return b.getAnnotation(annotationType);
+        return getParameterAccess(parameterName).getAnnotation(annotationType);
     }
 
     public boolean isRendering()
@@ -213,7 +253,7 @@
         return element.triggerEvent(eventType, defaulted(context), handler);
     }
 
-    private Object[] defaulted(Object[] input)
+    private static Object[] defaulted(Object[] input)
     {
         return input == null ? EMPTY : input;
     }
@@ -233,13 +273,6 @@
         return element.getContainingPage().getRootComponent();
     }
 
-    public boolean isInvariant(String parameterName)
-    {
-        Binding b = getBinding(parameterName);
-
-        return b != null && b.isInvariant();
-    }
-
     public boolean isLoaded()
     {
         return element.isLoaded();
@@ -265,86 +298,20 @@
         bindings.put(parameterName, binding);
     }
 
-    @SuppressWarnings("unchecked")
-    public <T> T readParameter(String parameterName, Class<T> expectedType)
-    {
-        Binding b = getBinding(parameterName);
-        T result;
-
-        try
-        {
-            // Will throw NPE if binding is null, but this should never be called if the
-            // parameter is not bound.
-
-            Object boundValue = b.get();
-
-            result = elementResources.coerce(boundValue, expectedType);
-        }
-        catch (Exception ex)
-        {
-            throw new TapestryException(StructureMessages.getParameterFailure(parameterName, getCompleteId(), ex), b,
-                                        ex);
-        }
-
-        if (result == null && !isAllowNull(parameterName))
-            throw new TapestryException(String.format(
-                    "Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.",
-                    parameterName,
-                    getCompleteId()), b, null);
-
-        return result;
-    }
-
-    private boolean isAllowNull(String parameterName)
-    {
-        ParameterModel parameterModel = getComponentModel().getParameterModel(parameterName);
-
-        return parameterModel == null ? true : parameterModel.isAllowNull();
-    }
-
-
-    public Object readParameter(String parameterName, String desiredTypeName)
-    {
-        Class parameterType = elementResources.toClass(desiredTypeName);
-
-        return readParameter(parameterName, parameterType);
-    }
 
     public Class getBoundType(String parameterName)
     {
-        Binding b = getBinding(parameterName);
-
-        return b != null ? b.getBindingType() : null;
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T> void writeParameter(String parameterName, T parameterValue)
-    {
-        Binding b = getBinding(parameterName);
-
-        Class bindingType = b.getBindingType();
-
-        try
-        {
-            Object coerced = elementResources.coerce(parameterValue, bindingType);
-
-            b.set(coerced);
-        }
-        catch (Exception ex)
-        {
-            throw new TapestryException(StructureMessages.writeParameterFailure(parameterName, getCompleteId(), ex), b,
-                                        ex);
-        }
+        return getParameterAccess(parameterName).getBoundType();
     }
 
     private Binding getBinding(String parameterName)
     {
-        return bindings == null ? null : bindings.get(parameterName);
+        return InternalUtils.get(bindings, parameterName);
     }
 
     public AnnotationProvider getAnnotationProvider(String parameterName)
     {
-        return getBinding(parameterName);
+        return getParameterAccess(parameterName);
     }
 
     public Logger getLogger()
@@ -361,27 +328,18 @@
     {
         if (bindings == null) return;
 
-        for (String name : bindings.keySet())
+        if (!informalsComputed)
         {
-            // Skip all formal parameters.
-
-            if (componentModel.getParameterModel(name) != null) continue;
-
-            Binding b = bindings.get(name);
-
-            Object value = b.get();
-
-            if (value == null) continue;
-
-            // Because Blocks can be passed in (right from the template),
-            // we want to skip those when rending informal parameters.
-
-            if (value instanceof Block) continue;
-
-            String valueString = elementResources.coerce(value, String.class);
+            for (Map.Entry<String, Binding> e : getInformalParameterBindings().entrySet())
+            {
+                firstInformal = new Informal(e.getKey(), e.getValue(), firstInformal);
+            }
 
-            writer.attributes(name, valueString);
+            informalsComputed = true;
         }
+
+        for (Informal i = firstInformal; i != null; i = i.next)
+            i.write(writer);
     }
 
     public Component getContainer()
@@ -470,9 +428,10 @@
     {
         Object result = InternalUtils.get(renderVariables, name);
 
-        if (result == null) throw new IllegalArgumentException(StructureMessages.missingRenderVariable(getCompleteId(),
-                                                                                                       name,
-                                                                                                       renderVariables == null ? null : renderVariables.keySet()));
+        if (result == null)
+            throw new IllegalArgumentException(StructureMessages.missingRenderVariable(getCompleteId(),
+                                                                                       name,
+                                                                                       renderVariables == null ? null : renderVariables.keySet()));
 
         return result;
     }
@@ -499,4 +458,109 @@
     {
         page.addLifecycleListener(listener);
     }
+
+    public ParameterAccess getParameterAccess(final String parameterName)
+    {
+        if (access == null) access = CollectionFactory.newCaseInsensitiveMap();
+
+        ParameterAccess result = access.get(parameterName);
+
+        if (result == null)
+        {
+            result = createParameterAccess(parameterName);
+            access.put(parameterName, result);
+        }
+
+        return result;
+    }
+
+    private ParameterAccess createParameterAccess(final String parameterName)
+    {
+        final Binding binding = getBinding(parameterName);
+
+        ParameterModel parameterModel = getComponentModel().getParameterModel(parameterName);
+
+        final boolean allowNull = parameterModel == null ? true : parameterModel.isAllowNull();
+
+        return new ParameterAccess()
+        {
+            public boolean isBound()
+            {
+                return binding != null;
+            }
+
+            public Object read(String desiredTypeName)
+            {
+                Class desiredType = elementResources.toClass(desiredTypeName);
+
+                return read(desiredType);
+            }
+
+            public <T> T read(Class<T> desiredType)
+            {
+                if (binding == null) return null;
+
+                T result;
+
+                try
+                {
+                    // Will throw NPE if binding is null, but this should never be called if the
+                    // parameter is not bound.
+
+                    Object boundValue = binding.get();
+
+                    result = elementResources.coerce(boundValue, desiredType);
+                }
+                catch (Exception ex)
+                {
+                    throw new TapestryException(
+                            StructureMessages.getParameterFailure(parameterName, getCompleteId(), ex), binding,
+                            ex);
+                }
+
+                if (result == null && !allowNull)
+                    throw new TapestryException(String.format(
+                            "Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.",
+                            parameterName,
+                            getCompleteId()), binding, null);
+
+                return result;
+            }
+
+            public <T> void write(T parameterValue)
+            {
+                if (binding == null) return;
+
+                Class bindingType = binding.getBindingType();
+
+                try
+                {
+                    Object coerced = elementResources.coerce(parameterValue, bindingType);
+
+                    binding.set(coerced);
+                }
+                catch (Exception ex)
+                {
+                    throw new TapestryException(
+                            StructureMessages.writeParameterFailure(parameterName, getCompleteId(), ex), binding,
+                            ex);
+                }
+            }
+
+            public boolean isInvariant()
+            {
+                return binding != null && binding.isInvariant();
+            }
+
+            public Class getBoundType()
+            {
+                return binding == null ? null : binding.getBindingType();
+            }
+
+            public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+            {
+                return binding == null ? null : binding.getAnnotation(annotationClass);
+            }
+        };
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StartElementPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StartElementPageElement.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StartElementPageElement.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StartElementPageElement.java Mon Dec 22 11:50:26 2008
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,12 +15,10 @@
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.runtime.RenderCommand;
 import org.apache.tapestry5.runtime.RenderQueue;
 
-/**
- *
- */
-public class StartElementPageElement implements PageElement
+public class StartElementPageElement implements RenderCommand
 {
     private final String namespaceURI;
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/TextPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/TextPageElement.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/TextPageElement.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/TextPageElement.java Mon Dec 22 11:50:26 2008
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,12 +15,10 @@
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.runtime.RenderCommand;
 import org.apache.tapestry5.runtime.RenderQueue;
 
-/**
- *
- */
-public class TextPageElement implements PageElement
+public class TextPageElement implements RenderCommand
 {
     private final String text;
 
@@ -39,5 +37,4 @@
     {
         return String.format("Text[%s]", text);
     }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java Mon Dec 22 11:50:26 2008
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.annotations.SetupRender;
 import org.apache.tapestry5.internal.services.ComponentResourcesOperation;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.SymbolSource;
@@ -94,6 +95,8 @@
         // the integration test, thank you.
 
         transformation.extendMethod(TransformConstants.SETUP_RENDER_SIGNATURE, body);
+
+        model.addRenderPhase(SetupRender.class);
     }
 
     /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EnvironmentalWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EnvironmentalWorker.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EnvironmentalWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EnvironmentalWorker.java Mon Dec 22 11:50:26 2008
@@ -15,11 +15,10 @@
 package org.apache.tapestry5.internal.transform;
 
 import org.apache.tapestry5.annotations.Environmental;
+import org.apache.tapestry5.ioc.services.Builtin;
+import org.apache.tapestry5.ioc.services.ClassFactory;
 import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.services.ClassTransformation;
-import org.apache.tapestry5.services.ComponentClassTransformWorker;
-import org.apache.tapestry5.services.Environment;
-import org.apache.tapestry5.services.TransformMethodSignature;
+import org.apache.tapestry5.services.*;
 
 import java.lang.reflect.Modifier;
 import java.util.List;
@@ -32,9 +31,13 @@
 {
     private final Environment environment;
 
-    public EnvironmentalWorker(Environment environment)
+    private final ClassLoader classLoader;
+
+    public EnvironmentalWorker(Environment environment, @Builtin ClassFactory servicesLayerClassFactory)
     {
         this.environment = environment;
+
+        classLoader = servicesLayerClassFactory.getClassLoader();
     }
 
     public void transform(ClassTransformation transformation, MutableComponentModel model)
@@ -56,23 +59,57 @@
         for (String name : names)
         {
             Environmental annotation = transformation.getFieldAnnotation(name, Environmental.class);
-            
+
             transformation.claimField(name, annotation);
 
-            String type = transformation.getFieldType(name);
+            String typeName = transformation.getFieldType(name);
 
             // TODO: Check for primitives
 
-            // Caching might be good for efficiency at some point.
+            // TAP5-417: Calls to javassist.runtime.Desc.getType() are showing up as method hot spots.
+
+            Class type = null;
+
+            try
+            {
+                type = classLoader.loadClass(typeName);
+            }
+            catch (ClassNotFoundException ex)
+            {
+                throw new RuntimeException(ex);
+            }
+
+            // TAP5-417: Changed the code to use EnvironmentalAccess, which encapsulates
+            // efficient caching.
+
+            String injectedTypeFieldName = transformation.addInjectedField(Class.class, "type", type);
+
+            // First we need (at page attach) to acquire the closure for the type.
+
+            String accessFieldName = transformation.addField(Modifier.PRIVATE, EnvironmentalAccess.class.getName(),
+                                                             name + "_access");
+
+            String attachBody = String.format("%s = %s.getAccess(%s);",
+                                              accessFieldName, envField, injectedTypeFieldName);
+
+            transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE, attachBody);
+
+            // Clear the closure field when the page detaches.  We'll get a new one when we next attach.
+
+            transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
+                                        accessFieldName + " = null;");
+
+            // Now build a read method that invokes peek() or peekRequired() on the closure. The closure
+            // is responsible for safe caching of the environmental value.
 
             String methodName = transformation.newMemberName("environment_read", name);
 
-            TransformMethodSignature sig = new TransformMethodSignature(Modifier.PRIVATE, type, methodName, null,
+            TransformMethodSignature sig = new TransformMethodSignature(Modifier.PRIVATE, typeName, methodName, null,
                                                                         null);
 
             String body = String.format(
-                    "return ($r) %s.%s($type);",
-                    envField,
+                    "return ($r) %s.%s();",
+                    accessFieldName,
                     annotation.value() ? "peekRequired" : "peek");
 
             transformation.addMethod(sig, body);
@@ -82,5 +119,4 @@
             transformation.removeField(name);
         }
     }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java Mon Dec 22 11:50:26 2008
@@ -17,6 +17,7 @@
 import org.apache.tapestry5.Binding;
 import org.apache.tapestry5.annotations.Parameter;
 import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.ParameterAccess;
 import org.apache.tapestry5.internal.bindings.LiteralBinding;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.util.BodyBuilder;
@@ -93,14 +94,15 @@
 
         String resourcesFieldName = transformation.getResourcesFieldName();
 
-        String invariantFieldName = addParameterSetup(name, annotation.defaultPrefix(), annotation.value(),
-                                                      parameterName, cachedFieldName, cache, type, resourcesFieldName,
-                                                      transformation, annotation.autoconnect());
+        String accessFieldName = addParameterSetup(name, annotation.defaultPrefix(), annotation.value(),
+                                                   parameterName, cachedFieldName, cache, type, resourcesFieldName,
+                                                   transformation, annotation.autoconnect());
 
-        addReaderMethod(name, cachedFieldName, invariantFieldName, cache, parameterName, type, resourcesFieldName,
+        addReaderMethod(name, cachedFieldName, accessFieldName, cache, parameterName, type, resourcesFieldName,
                         transformation);
 
-        addWriterMethod(name, cachedFieldName, cache, parameterName, type, resourcesFieldName, transformation);
+        addWriterMethod(name, cachedFieldName, accessFieldName, cache, parameterName, type, resourcesFieldName,
+                        transformation);
     }
 
     /**
@@ -110,16 +112,22 @@
                                      String parameterName, String cachedFieldName, boolean cache, String fieldType,
                                      String resourcesFieldName, ClassTransformation transformation, boolean autoconnect)
     {
-        String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName + "_default");
 
-        String invariantFieldName = transformation.addField(Modifier.PRIVATE, "boolean", fieldName + "_invariant");
+        String accessFieldName = transformation.addField(Modifier.PRIVATE, ParameterAccess.class.getName(),
+                                                         fieldName + "_access");
+
+        String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName + "_default");
 
         BodyBuilder builder = new BodyBuilder().begin();
 
-        addDefaultBindingSetup(parameterName, defaultPrefix, defaultBinding, resourcesFieldName, transformation,
+        addDefaultBindingSetup(parameterName, defaultPrefix, defaultBinding, resourcesFieldName,
+                               transformation,
                                builder, autoconnect);
 
-        builder.addln("%s = %s.isInvariant(\"%s\");", invariantFieldName, resourcesFieldName, parameterName);
+        // Order is (alas) important here: must invoke getParameterAccess() after the binding setup, as
+        // that code may invoke InternalComponentResources.bindParameter().
+
+        builder.addln("%s = %s.getParameterAccess(\"%s\");", accessFieldName, resourcesFieldName, parameterName);
 
         // Store the current value of the field into the default field. This value will
         // be used to reset the field after rendering.
@@ -139,7 +147,7 @@
         {
             builder.clear();
 
-            builder.addln("if (! %s)", invariantFieldName);
+            builder.addln("if (! %s.isInvariant())", accessFieldName);
             builder.begin();
             builder.addln("%s = %s;", fieldName, defaultFieldName);
             builder.addln("%s = false;", cachedFieldName);
@@ -156,11 +164,12 @@
             transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE, builder.toString());
         }
 
-        return invariantFieldName;
+        return accessFieldName;
     }
 
     private void addDefaultBindingSetup(String parameterName, String defaultPrefix, String defaultBinding,
-                                        String resourcesFieldName, ClassTransformation transformation,
+                                        String resourcesFieldName,
+                                        ClassTransformation transformation,
                                         BodyBuilder builder, boolean autoconnect)
     {
         if (InternalUtils.isNonBlank(defaultBinding))
@@ -192,13 +201,14 @@
         // If no default binding expression provided in the annotation, then look for a default
         // binding method to provide the binding.
 
-        final String methodName = "default" + InternalUtils.capitalize(parameterName);
+        final String methodName = "default" + parameterName;
 
         MethodFilter filter = new MethodFilter()
         {
             public boolean accept(TransformMethodSignature signature)
             {
-                return signature.getParameterTypes().length == 0 && signature.getMethodName().equals(methodName);
+                return signature.getParameterTypes().length == 0
+                        && signature.getMethodName().equalsIgnoreCase(methodName);
             }
         };
 
@@ -209,12 +219,23 @@
 
         if (signatures.isEmpty()) return;
 
+        // Because the check was case-insensitive, we need to determine the actual
+        // name.
+
+        String actualMethodName = signatures.get(0).getMethodName();
+
         builder.addln("if (! %s.isBound(\"%s\"))", resourcesFieldName, parameterName);
-        builder.addln("  %s(\"%s\", %s, ($w) %s());", BIND_METHOD_NAME, parameterName, resourcesFieldName, methodName);
+        builder.addln("  %s(\"%s\", %s, ($w) %s());",
+                      BIND_METHOD_NAME,
+                      parameterName,
+                      resourcesFieldName,
+                      actualMethodName);
     }
 
-    private void addWriterMethod(String fieldName, String cachedFieldName, boolean cache, String parameterName,
-                                 String fieldType, String resourcesFieldName, ClassTransformation transformation)
+    private void addWriterMethod(String fieldName, String cachedFieldName, String accessFieldName, boolean cache,
+                                 String parameterName,
+                                 String fieldType, String resourcesFieldName,
+                                 ClassTransformation transformation)
     {
         BodyBuilder builder = new BodyBuilder();
         builder.begin();
@@ -233,8 +254,7 @@
         // read-only or unbound parameters. $1 is the single parameter
         // to the method.
 
-        builder.addln("if (%s.isBound(\"%s\"))", resourcesFieldName, parameterName);
-        builder.addln("  %s.writeParameter(\"%s\", ($w)$1);", resourcesFieldName, parameterName);
+        builder.addln("%s.write(($w)$1);", accessFieldName);
 
         builder.addln("%s = $1;", fieldName);
 
@@ -255,7 +275,7 @@
     /**
      * Adds a private method that will be the replacement for read-access to the field.
      */
-    private void addReaderMethod(String fieldName, String cachedFieldName, String invariantFieldName, boolean cache,
+    private void addReaderMethod(String fieldName, String cachedFieldName, String accessFieldName, boolean cache,
                                  String parameterName, String fieldType, String resourcesFieldName,
                                  ClassTransformation transformation)
     {
@@ -265,8 +285,8 @@
         // While the component is still loading, or when the value for the component is cached,
         // or if the value is not bound, then return the current value of the field.
 
-        builder.addln("if (%s || ! %s.isLoaded() || ! %<s.isBound(\"%s\")) return %s;", cachedFieldName,
-                      resourcesFieldName, parameterName, fieldName);
+        builder.addln("if (%s || ! %s.isLoaded() || ! %s.isBound()) return %s;", cachedFieldName,
+                      resourcesFieldName, accessFieldName, fieldName);
 
         String cast = TransformUtils.getWrapperTypeName(fieldType);
 
@@ -276,14 +296,13 @@
         // to readParameter(), since its easier to convert it properly to
         // a type on that end than in the generated code.
 
-        builder.addln("%s result = ($r) ((%s) %s.readParameter(\"%s\", \"%2$s\"));", fieldType, cast,
-                      resourcesFieldName, parameterName);
+        builder.addln("%s result = ($r) ((%s) %s.read(\"%2$s\"));", fieldType, cast, accessFieldName);
 
         // If the binding is invariant, then it's ok to cache. Othewise, its only
         // ok to cache if a) the @Parameter says to cache and b) the component
         // is rendering at the point when field is accessed.
 
-        builder.add("if (%s", invariantFieldName);
+        builder.add("if (%s.isInvariant()", accessFieldName);
 
         if (cache) builder.add(" || %s.isRendering()", resourcesFieldName);
 
@@ -313,6 +332,9 @@
         return InternalUtils.stripMemberName(fieldName);
     }
 
+    /**
+     * Invoked from generated code as part of the handling of parameter default methods.
+     */
     public static void bind(String parameterName, InternalComponentResources resources, Object value)
     {
         if (value == null) return;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java Mon Dec 22 11:50:26 2008
@@ -112,6 +112,8 @@
 
         if (methods.isEmpty()) return;
 
+        model.addRenderPhase(methodAnnotation);
+
         BodyBuilder builder = new BodyBuilder();
         builder.begin();
 
@@ -188,5 +190,4 @@
         else
             builder.addln(";");
     }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java Mon Dec 22 11:50:26 2008
@@ -21,6 +21,7 @@
 import org.slf4j.Logger;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Defines a component in terms of its capabilities, parameters, sub-components, etc. During <em>runtime</em>, the
@@ -146,4 +147,13 @@
      * @return the value for the key (possibly inherited from a parent model), or null
      */
     String getMeta(String key);
+
+    /**
+     * Returns a set of all the render phases that this model (including parent models) that are handled. Render phases
+     * are represented by the corresponding annotation ({@link org.apache.tapestry5.annotations.BeginRender}, {@link
+     * org.apache.tapestry5.annotations.AfterRender}, etc.).
+     *
+     * @return set of classes
+     */
+    Set<Class> getHandledRenderPhases();
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java Mon Dec 22 11:50:26 2008
@@ -85,4 +85,13 @@
      * Stores a meta data value under the indicated key.
      */
     void setMeta(String key, String value);
+
+    /**
+     * Identifies that the component does handle the render phase.
+     *
+     * @param renderPhase annotation class corresponding to the render phase
+     * @see ComponentModel#getHandledRenderPhases()
+     * @since 5.0.19
+     */
+    void addRenderPhase(Class renderPhase);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Environment.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Environment.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Environment.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Environment.java Mon Dec 22 11:50:26 2008
@@ -72,4 +72,16 @@
      * Clears all stacks; used when initializing the Environment before a render.
      */
     void clear();
+
+    /**
+     * For some type, returns a temporary access object for the type. The access object is efficient because it ties
+     * directly to the thread's instance of the Environment service, it also caches the current value. <p>The access
+     * object must be discarded at the end of the request (it will be unusable at that point anyway).
+     *
+     * @param type type of environmental object
+     * @param <T>
+     * @return access object that can be used to see the current environmental object of the type
+     * @since 5.0.19
+     */
+    <T> EnvironmentalAccess<T> getAccess(Class<T> type);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Mon Dec 22 11:50:26 2008
@@ -149,7 +149,6 @@
         binder.bind(ApplicationGlobals.class, ApplicationGlobalsImpl.class);
         binder.bind(AssetSource.class, AssetSourceImpl.class);
         binder.bind(Cookies.class, CookiesImpl.class);
-        binder.bind(Environment.class, EnvironmentImpl.class);
         binder.bind(FieldValidatorDefaultSource.class, FieldValidatorDefaultSourceImpl.class);
         binder.bind(RequestGlobals.class, RequestGlobalsImpl.class);
         binder.bind(ResourceDigestGenerator.class, ResourceDigestGeneratorImpl.class);
@@ -1988,6 +1987,7 @@
         configuration.add("li", RelativeElementPosition.INSIDE);
     }
 
+
     /**
      * @since 5.1
      */
@@ -2032,4 +2032,14 @@
 
         return service;
     }
+
+    @Scope(ScopeConstants.PERTHREAD)
+    public Environment buildEnvironment(PerthreadManager perthreadManager)
+    {
+        EnvironmentImpl service = new EnvironmentImpl();
+
+        perthreadManager.addThreadCleanupListener(service);
+
+        return service;
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java Mon Dec 22 11:50:26 2008
@@ -22,13 +22,17 @@
 
 /**
  * Constants used by implementations of {@link org.apache.tapestry5.services.ComponentClassTransformWorker}.
+ * <p/>
+ * Note: methods on transformed components will not be invoked <em>unless</em> {@linkplain
+ * org.apache.tapestry5.model.MutableComponentModel#addRenderPhase(Class) the component model is updated to identify the
+ * use of the corresponding render phase}.
  */
 public final class TransformConstants
 {
     // Shared parameters of a whole bunch of lifecycle methods, representing the different
     // component render states.
-    private static final String[] RENDER_PHASE_METHOD_PARAMETERS = { MarkupWriter.class.getName(),
-            Event.class.getName() };
+    private static final String[] RENDER_PHASE_METHOD_PARAMETERS = {MarkupWriter.class.getName(),
+            Event.class.getName()};
 
     /**
      * Signature for {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}.
@@ -36,7 +40,7 @@
      * @see org.apache.tapestry5.annotations.OnEvent
      */
     public static final TransformMethodSignature DISPATCH_COMPONENT_EVENT = new TransformMethodSignature(
-            Modifier.PUBLIC, "boolean", "dispatchComponentEvent", new String[] { ComponentEvent.class.getName() },
+            Modifier.PUBLIC, "boolean", "dispatchComponentEvent", new String[] {ComponentEvent.class.getName()},
             null);
 
     /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java Mon Dec 22 11:50:26 2008
@@ -645,7 +645,7 @@
     protected final void train_getParameterModel(ComponentModel model, String parameterName,
                                                  ParameterModel parameterModel)
     {
-        expect(model.getParameterModel(parameterName)).andReturn(parameterModel);
+        expect(model.getParameterModel(parameterName)).andReturn(parameterModel).atLeastOnce();
     }
 
     protected final void train_getParameterNames(ComponentModel model, String... names)
@@ -1180,4 +1180,9 @@
     {
         expect(formSupport.getFormValidationId()).andReturn(validationId).atLeastOnce();
     }
+
+    protected final void train_isAllowNull(ParameterModel model, boolean allowNull)
+    {
+        expect(model.isAllowNull()).andReturn(allowNull).atLeastOnce();
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/corelib/components/LoopTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/corelib/components/LoopTest.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/corelib/components/LoopTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/corelib/components/LoopTest.java Mon Dec 22 11:50:26 2008
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.corelib.components;
 
+import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.services.Heartbeat;
 import org.easymock.EasyMock;
@@ -28,6 +29,7 @@
     public void non_empty_iterator()
     {
         Heartbeat hb = mockHeartbeat();
+        MarkupWriter writer = mockMarkupWriter();
 
         // Really hard to test the exact timing of all this; it will have to
         // be "proven" by integration tests.
@@ -49,21 +51,21 @@
         assertTrue(loop.setup());
         assertEquals(loop.getIndex(), 0);
 
-        loop.begin();
+        loop.begin(writer);
         assertEquals(loop.getValue(), "alpha");
         assertEquals(loop.getIndex(), 0);
 
-        assertFalse(loop.after());
-        loop.begin();
+        assertFalse(loop.after(writer));
+        loop.begin(writer);
         assertEquals(loop.getValue(), "beta");
         assertEquals(loop.getIndex(), 1);
 
-        assertFalse(loop.after());
-        loop.begin();
+        assertFalse(loop.after(writer));
+        loop.begin(writer);
         assertEquals(loop.getValue(), "gamma");
         assertEquals(loop.getIndex(), 2);
 
-        assertTrue(loop.after());
+        assertTrue(loop.after(writer));
 
         verify();
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java Mon Dec 22 11:50:26 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.model;
 
 import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.annotations.BeginRender;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.Resource;
@@ -548,7 +549,6 @@
         }
 
         verify();
-
     }
 
     @Test
@@ -722,4 +722,65 @@
 
         verify();
     }
+
+    /**
+     * @since 5.0.19
+     */
+    @Test
+    public void does_not_handle_render_phase_and_no_parent()
+    {
+        Resource r = mockResource();
+        Logger logger = mockLogger();
+
+        replay();
+
+        MutableComponentModel model = new MutableComponentModelImpl(CLASS_NAME, logger, r, null);
+
+
+        assertFalse(model.getHandledRenderPhases().contains(BeginRender.class));
+
+        verify();
+    }
+
+    /**
+     * @since 5.0.19
+     */
+    @Test
+    public void handles_render_phase()
+    {
+        Resource r = mockResource();
+        Logger logger = mockLogger();
+
+        replay();
+
+        MutableComponentModel model = new MutableComponentModelImpl(CLASS_NAME, logger, r, null);
+
+        model.addRenderPhase(BeginRender.class);
+
+        assertTrue(model.getHandledRenderPhases().contains(BeginRender.class));
+
+        verify();
+    }
+
+    /**
+     * @since 5.0.19
+     */
+    @Test
+    public void parent_handles_render_phase()
+    {
+        Resource r = mockResource();
+        Logger logger = mockLogger();
+
+        replay();
+
+        MutableComponentModel parent = new MutableComponentModelImpl(CLASS_NAME, logger, r, null);
+        MutableComponentModel child = new MutableComponentModelImpl(CLASS_NAME, logger, r, parent);
+
+        parent.addRenderPhase(BeginRender.class);
+
+
+        assertTrue(child.getHandledRenderPhases().contains(BeginRender.class));
+
+        verify();
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java Mon Dec 22 11:50:26 2008
@@ -50,12 +50,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("eventType", "someId", 0));
         assertFalse(event.matches("foo", "someId", 0));
@@ -68,12 +71,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("EVENTTYPE", "someid", 0));
 
@@ -85,12 +91,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("eventType", "someId", 0));
 
@@ -104,12 +113,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("eventtype", "SOMEID", 0));
 
@@ -123,6 +135,9 @@
         ComponentPageElementResources resources = mockComponentPageElementResources();
         EventContext context = mockEventContext();
         Integer value = new Integer(27);
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_toClass(resources, "java.lang.Integer", Integer.class);
 
@@ -131,7 +146,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources, logger);
 
         assertSame(event.coerceContext(0, "java.lang.Integer"), value);
 
@@ -145,7 +160,8 @@
         EventContext context = mockEventContext();
         Logger logger = mockLogger();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        train_isDebugEnabled(logger, true);
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         train_getCount(context, 0);
 
@@ -176,7 +192,8 @@
         ComponentPageElementResources resources = mockComponentPageElementResources();
         Logger logger = mockLogger();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        train_isDebugEnabled(logger, true);
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         train_toClass(resources, Integer.class.getName(), Integer.class);
 
@@ -213,7 +230,10 @@
         String methodDescription = "foo.Bar.baz()";
         Logger logger = mockLogger();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        train_isDebugEnabled(logger, true);
+        EasyMock.expectLastCall().atLeastOnce();
+
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         ComponentEventCallback handler = mockComponentEventHandler();
 
@@ -242,7 +262,8 @@
         ComponentEventCallback handler = mockComponentEventHandler();
         Logger logger = mockLogger();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        train_isDebugEnabled(logger, true);
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         train_handleResult(handler, result, false);
 
@@ -265,7 +286,8 @@
         ComponentEventCallback handler = mockComponentEventHandler();
         Logger logger = mockLogger();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        train_isDebugEnabled(logger, true);
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         replay();
 
@@ -288,7 +310,10 @@
         ComponentEventCallback handler = mockComponentEventHandler();
         Logger logger = mockLogger();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        train_isDebugEnabled(logger, true);
+        EasyMock.expectLastCall().atLeastOnce();
+
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         EasyMock.expectLastCall().atLeastOnce();
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageElementFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageElementFactoryImplTest.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageElementFactoryImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageElementFactoryImplTest.java Mon Dec 22 11:50:26 2008
@@ -19,11 +19,11 @@
 import org.apache.tapestry5.dom.MarkupModel;
 import org.apache.tapestry5.dom.XMLMarkupModel;
 import org.apache.tapestry5.internal.parser.AttributeToken;
-import org.apache.tapestry5.internal.structure.PageElement;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.runtime.RenderCommand;
 import org.apache.tapestry5.runtime.RenderQueue;
 import org.apache.tapestry5.services.BindingSource;
 import org.apache.tapestry5.services.ComponentClassResolver;
@@ -47,7 +47,7 @@
         PageElementFactory factory = new PageElementFactoryImpl(source, resolver, null, null, null);
         AttributeToken token = new AttributeToken(null, "name", "value", l);
 
-        PageElement element = factory.newAttributeElement(null, token);
+        RenderCommand element = factory.newAttributeElement(null, token);
 
         writer.element("root");
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/BlockImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/BlockImplTest.java?rev=728754&r1=728753&r2=728754&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/BlockImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/BlockImplTest.java Mon Dec 22 11:50:26 2008
@@ -20,6 +20,7 @@
 import org.apache.tapestry5.ioc.Resource;
 import org.apache.tapestry5.ioc.internal.util.ClasspathResource;
 import org.apache.tapestry5.ioc.internal.util.LocationImpl;
+import org.apache.tapestry5.runtime.RenderCommand;
 import org.apache.tapestry5.runtime.RenderQueue;
 import org.testng.annotations.Test;
 
@@ -45,8 +46,8 @@
         BlockImpl block = new BlockImpl(null, null);
         RenderQueue queue = mockRenderQueue();
         MarkupWriter writer = mockMarkupWriter();
-        PageElement element1 = mockPageElement();
-        PageElement element2 = mockPageElement();
+        RenderCommand element1 = mockRenderCommand();
+        RenderCommand element2 = mockRenderCommand();
 
         getMocksControl().checkOrder(true);