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

svn commit: r437852 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/annotations/ main/java/org/apache/tapestry/internal/ main/java/org/apache/tapestry/internal/parser/ main/java/org/apache/...

Author: hlship
Date: Mon Aug 28 14:43:42 2006
New Revision: 437852

URL: http://svn.apache.org/viewvc?rev=437852&view=rev
Log:
Add many toString() implementations.
Add support for component bodies and the BeforeRenderBody component render phase.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/BeforeRenderBody.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BodyToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Strong.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/body_element.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/content_within_body_element.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/illegal_nesting_within_body_element.html
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/RenderTag.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ClasspathResource.java
    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/TextToken.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/services/InternalClassTransformationImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RenderQueueImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/AttributePageElement.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/Page.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StartElementPageElement.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/TextPageElement.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderCommand.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderQueue.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/tapestry_5_0_0.xsd
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Start.html

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java Mon Aug 28 14:43:42 2006
@@ -29,6 +29,13 @@
      */
     String getId();
 
+    /**
+     * Returns a string consisting of the name of the containing page, plus the concatinated ids of
+     * all containing components, separated by periods. I.e., "com.foo.pages.MyPage:foo.bar.baz".
+     */
+
+    String getCompleteId();
+
     /** Returns the component model object that defines the behavior of the component. */
     ComponentModel getComponentModel();
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/BeforeRenderBody.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/BeforeRenderBody.java?rev=437852&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/BeforeRenderBody.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/BeforeRenderBody.java Mon Aug 28 14:43:42 2006
@@ -0,0 +1,46 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Marks methods to be invoked when the component rendering state machine hits the point in the
+ * component's template where the body element occurs. Such methods may optionally take a
+ * {@link org.apache.tapestry.MarkupWriter} parameter, and may return void or boolean.
+ * <p>
+ * Returning true (or void) will queue up the component's body for rendering. Once the body and the
+ * rest of the component's template renders, the
+ * {@link org.apache.tapestry.annotations.RenderCloseTag} phase will execute.
+ * <p>
+ * Returning false will skip the component's body, but continue rendering the template. The
+ * {@link org.apache.tapestry.annotations.RenderCloseTag} phase will still execute after the
+ * template finishes rendering.
+ * <p>
+ * This phase is skipped for components which do not have a body.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Target(ElementType.METHOD)
+@Retention(RUNTIME)
+@Documented
+public @interface BeforeRenderBody {
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/RenderTag.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/RenderTag.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/RenderTag.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/RenderTag.java Mon Aug 28 14:43:42 2006
@@ -25,8 +25,14 @@
  * Marker annotation for methods that should be executed during the the rendering of the tag phase.
  * Such methods may optionally take a {@link org.apache.tapestry.MarkupWriter} parameter, and may
  * return void or boolean. Returning true or void will allow the component to advance into the
- * render template / render body phase. Return false will skip rendering of the template and body,
- * and jump directly to the AfterRender phase.
+ * render template / render body phase. If a body is present, the
+ * {@link org.apache.tapestry.annotations.BeforeRenderBody} phase will execute. Either way, the
+ * {@link org.apache.tapestry.annotations.RenderCloseTag} phase will execute after the template (and
+ * body) have rendered. A component with a body but without a template will still see the
+ * {@link org.apache.tapestry.annotations.BeforeRenderBody} phase execute.
+ * <p>
+ * Returning false will skip rendering of the template and body, and jump directly to the
+ * {@link org.apache.tapestry.annotations.AfterRender} phase.
  * 
  * @author Howard M. Lewis Ship
  */

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ClasspathResource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ClasspathResource.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ClasspathResource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ClasspathResource.java Mon Aug 28 14:43:42 2006
@@ -99,4 +99,10 @@
         return null;
     }
 
+    @Override
+    public String toString()
+    {
+        return "classpath:" + _path;
+    }
+
 }

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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -46,4 +46,10 @@
     {
         return _value;
     }
+
+    @Override
+    public String toString()
+    {
+        return String.format("Attribute[%s=%s]", _name, _value);
+    }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BodyToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BodyToken.java?rev=437852&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BodyToken.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BodyToken.java Mon Aug 28 14:43:42 2006
@@ -0,0 +1,31 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.parser;
+
+import org.apache.tapestry.Location;
+
+/**
+ * Placeholder for a component's body (within the component's template).
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class BodyToken extends TemplateToken
+{
+    public BodyToken(Location location)
+    {
+        super(TokenType.BODY, location);
+    }
+
+}

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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -37,4 +37,10 @@
     {
         return _text;
     }
+
+    @Override
+    public String toString()
+    {
+        return String.format("CDATA[%s]", _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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -37,4 +37,9 @@
         return _comment;
     }
 
+    @Override
+    public String toString()
+    {
+        return String.format("Comment[%s]", _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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -27,4 +27,10 @@
     {
         super(TokenType.END_ELEMENT, location);
     }
+
+    @Override
+    public String toString()
+    {
+        return "End";
+    }
 }

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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -46,7 +46,7 @@
         super(TokenType.START_COMPONENT, location);
 
         // TODO: id or type may be null, nut not both!
-        
+
         _id = id;
         _type = type;
     }
@@ -61,4 +61,11 @@
         return _type;
     }
 
+    @Override
+    public String toString()
+    {
+        return String.format("StartComponent[id=%s, type=%s]", _id, _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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -44,4 +44,9 @@
         return _name;
     }
 
+    @Override
+    public String toString()
+    {
+        return String.format("Start[%s]", _name);
+    }
 }

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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -32,4 +32,10 @@
     {
         return _text;
     }
+
+    @Override
+    public String toString()
+    {
+        return String.format("Text[%s]", _text);
+    }
 }

Modified: 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=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TokenType.java Mon Aug 28 14:43:42 2006
@@ -22,5 +22,5 @@
  */
 public enum TokenType {
 
-    ATTRIBUTE, CDATA, COMMENT, END_ELEMENT, START_COMPONENT, START_ELEMENT, TEXT
+    ATTRIBUTE, CDATA, COMMENT, END_ELEMENT, START_COMPONENT, START_ELEMENT, TEXT, BODY
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java Mon Aug 28 14:43:42 2006
@@ -984,7 +984,7 @@
     @Override
     public String toString()
     {
-        StringBuilder builder = new StringBuilder("InternalClassTransformationImpl<\n");
+        StringBuilder builder = new StringBuilder("InternalClassTransformation[\n");
 
         try
         {
@@ -1015,7 +1015,7 @@
             builder.append(ex);
         }
 
-        builder.append(">");
+        builder.append("]");
 
         return builder.toString();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Mon Aug 28 14:43:42 2006
@@ -14,11 +14,15 @@
 
 package org.apache.tapestry.internal.services;
 
+import java.lang.annotation.Annotation;
+
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.annotations.AfterRender;
 import org.apache.tapestry.annotations.BeforeRender;
+import org.apache.tapestry.annotations.BeforeRenderBody;
 import org.apache.tapestry.annotations.RenderCloseTag;
 import org.apache.tapestry.annotations.RenderTag;
+import org.apache.tapestry.ioc.IOCUtilities;
 import org.apache.tapestry.ioc.LogSource;
 import org.apache.tapestry.ioc.OrderedConfiguration;
 import org.apache.tapestry.ioc.annotations.Contribute;
@@ -32,6 +36,7 @@
 import org.apache.tapestry.services.ComponentClassResolver;
 import org.apache.tapestry.services.ComponentClassTransformWorker;
 import org.apache.tapestry.services.MarkupWriterFactory;
+import org.apache.tapestry.services.MethodSignature;
 import org.apache.tapestry.services.TransformConstants;
 import org.apache.tapestry.services.WebRequestFilter;
 
@@ -97,9 +102,9 @@
     }
 
     @Lifecycle("perthread")
-    public TemplateParser buildTemplateParser()
+    public TemplateParser buildTemplateParser(Log log)
     {
-        return new TemplateParserImpl();
+        return new TemplateParserImpl(log);
     }
 
     public PageElementFactory buildPageElementFactory(
@@ -124,12 +129,14 @@
     {
         PagePoolImpl service = new PagePoolImpl(pageLoader);
 
-        // This covers invalidations due to changes to classes; we also
-        // need to invalidate when changes to templates occur. The TemplateLoader
-        // will ultimately be an invalidation event hub as well.
+        // This covers invalidations due to changes to classes
 
         pageLoader.addInvalidationListener(service);
 
+        // ... and this covers invalidations due to changes to templates
+
+        _componentTemplateSource.addInvalidationListener(service);
+
         return service;
     }
 
@@ -150,17 +157,24 @@
         configuration.add("Retain", new RetainWorker());
         configuration.add("UnclaimedField", new UnclaimedFieldWorker(), "after:*.*");
 
-        // Workers for the component rendering state machine methods
+        // Workers for the component rendering state machine methods; this is in typical
+        // execution order.
+
+        add(configuration, TransformConstants.BEFORE_RENDER_SIGNATURE, BeforeRender.class);
+        add(configuration, TransformConstants.RENDER_TAG_SIGNATURE, RenderTag.class);
+        add(configuration, TransformConstants.BEFORE_RENDER_BODY_SIGNATURE, BeforeRenderBody.class);
+        add(configuration, TransformConstants.RENDER_CLOSE_TAG_SIGNATURE, RenderCloseTag.class);
+        add(configuration, TransformConstants.AFTER_RENDER_SIGNATURE, AfterRender.class);
+    }
+
+    private void add(OrderedConfiguration<ComponentClassTransformWorker> configuration,
+            MethodSignature signature, Class<? extends Annotation> annotationClass)
+    {
+        // make the name match the annotation class name.
 
-        configuration.add("BeforeRender", new ComponentLifecycleMethodWorker(
-                TransformConstants.BEFORE_RENDER_SIGNATURE, BeforeRender.class));
-        configuration.add("RenderTag", new ComponentLifecycleMethodWorker(
-                TransformConstants.RENDER_TAG_SIGNATURE, RenderTag.class));
-        configuration.add("RenderCloseTag", new ComponentLifecycleMethodWorker(
-                TransformConstants.RENDER_CLOSE_TAG_SIGNATURE, RenderCloseTag.class));
-        configuration.add("AfterRender", new ComponentLifecycleMethodWorker(
-                TransformConstants.AFTER_RENDER_SIGNATURE, AfterRender.class));
+        String name = IOCUtilities.toSimpleId(annotationClass.getName());
 
+        configuration.add(name, new ComponentLifecycleMethodWorker(signature, annotationClass));
     }
 
     /**

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java Mon Aug 28 14:43:42 2006
@@ -15,7 +15,6 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.internal.parser.AttributeToken;
-import org.apache.tapestry.internal.parser.EndElementToken;
 import org.apache.tapestry.internal.parser.StartComponentToken;
 import org.apache.tapestry.internal.parser.StartElementToken;
 import org.apache.tapestry.internal.parser.TextToken;
@@ -36,11 +35,12 @@
 
     PageElement newAttributeElement(AttributeToken token);
 
-    PageElement newEndElement(EndElementToken token);
+    PageElement newEndElement();
 
     ComponentPageElement newComponentElement(Page page, ComponentPageElement container,
             StartComponentToken token);
 
     ComponentPageElement newRootComponentElement(Page page, String componentName);
 
+    PageElement newRenderBodyElement(ComponentPageElement component);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java Mon Aug 28 14:43:42 2006
@@ -16,7 +16,6 @@
 
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.internal.parser.AttributeToken;
-import org.apache.tapestry.internal.parser.EndElementToken;
 import org.apache.tapestry.internal.parser.StartComponentToken;
 import org.apache.tapestry.internal.parser.StartElementToken;
 import org.apache.tapestry.internal.parser.TextToken;
@@ -54,6 +53,12 @@
         {
             writer.end();
         }
+
+        @Override
+        public String toString()
+        {
+            return "End";
+        }
     };
 
     public PageElement newStartElement(StartElementToken token)
@@ -66,7 +71,7 @@
         return new TextPageElement(token.getText());
     }
 
-    public PageElement newEndElement(EndElementToken token)
+    public PageElement newEndElement()
     {
         return _endElement;
     }
@@ -106,6 +111,24 @@
         ComponentModel model = _componentInstantiatorSource.findComponentModel(componentName);
 
         return new ComponentPageElementImpl(page, instantiator, model);
+    }
+
+    public PageElement newRenderBodyElement(final ComponentPageElement component)
+    {
+        return new PageElement()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+                component.enqueueBeforeRenderBody(queue);
+            }
+
+            @Override
+            public String toString()
+            {
+                // TODO: Change this to be nested id
+                return String.format("RenderBody[%s]", component.getId());
+            }
+        };
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java Mon Aug 28 14:43:42 2006
@@ -22,7 +22,6 @@
 import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry.internal.parser.AttributeToken;
 import org.apache.tapestry.internal.parser.ComponentTemplate;
-import org.apache.tapestry.internal.parser.EndElementToken;
 import org.apache.tapestry.internal.parser.StartComponentToken;
 import org.apache.tapestry.internal.parser.StartElementToken;
 import org.apache.tapestry.internal.parser.TemplateToken;
@@ -121,12 +120,15 @@
                 .getComponentModel().getComponentClassName(), _locale);
 
         // Perhaps when template is null, we should act as if the template
-        // consists of just a render body element?  Also, pages should (probably?)
+        // consists of just a render body element? Also, pages should (probably?)
         // be required to have a template.
-        
+
         if (template == null)
-            return;        
-        
+        {
+            addRenderBodyElement(loadingComponent);
+            return;
+        }
+
         // Here's the thing. Stuff in this template is part of element's template unless
         // it is inside another component, in which case, it is part of the component's
         // body. template is stuff from the component's own template (if any). body
@@ -153,15 +155,15 @@
 
                 case START_ELEMENT:
 
-                    // We push onto *both* stacks when starting an element or when
-                    // starting a component, because we can't differentitate cases
-                    // when we hit the end element token.
-
                     PageElement newElement = _pageElementFactory
                             .newStartElement((StartElementToken) token);
 
                     add(loadingComponent, activeComponent, newElement);
 
+                    // We push onto *both* stacks when starting an element or when
+                    // starting a component, because we can't differentitate cases
+                    // when we hit the end element token.
+
                     push(activePageElementStack, activePageElement);
                     push(activeComponentStack, activeComponent);
 
@@ -175,10 +177,23 @@
 
                     break;
 
+                case BODY:
+
+                    addRenderBodyElement(loadingComponent);
+
+                    // BODY tokens are *not* matched by END_ELEMENT tokens. Nor will there be
+                    // text or comment content "inside" the BODY.
+
+                    break;
+
                 case END_ELEMENT:
 
-                    add(loadingComponent, activeComponent, _pageElementFactory
-                            .newEndElement((EndElementToken) token));
+                    // Filter out the end element for an embedded component. That's not part of the
+                    // component's body or the containing component's template (just the RenderBody
+                    // page element is part of the component's template).
+
+                    if (activePageElement != activeComponent || activeComponent == loadingComponent)
+                        add(loadingComponent, activeComponent, _pageElementFactory.newEndElement());
 
                     activePageElement = pop(activePageElementStack);
                     activeComponent = pop(activeComponentStack);
@@ -197,7 +212,7 @@
                             ((StartComponentToken) token));
 
                     add(loadingComponent, activeComponent, newComponent);
-                    
+
                     // Make sure container knows about the new component.
 
                     loadingComponent.addChild(newComponent);
@@ -240,6 +255,11 @@
 
         }
 
+    }
+
+    private void addRenderBodyElement(ComponentPageElement loadingComponent)
+    {
+        loadingComponent.addToTemplate(_pageElementFactory.newRenderBodyElement(loadingComponent));
     }
 
     private void add(ComponentPageElement loadingComponent, ComponentPageElement activeComponent,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java Mon Aug 28 14:43:42 2006
@@ -38,9 +38,9 @@
     {
         MarkupWriter writer = _markupWriterFactory.newMarkupWriter();
 
-        RenderQueueImpl queue = new RenderQueueImpl();
+        RenderQueueImpl queue = new RenderQueueImpl(page.getLog());
 
-        queue.addFirst(page.getRootElement());
+        queue.push(page.getRootElement());
 
         // Run the queue until empty.
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RenderQueueImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RenderQueueImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RenderQueueImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RenderQueueImpl.java Mon Aug 28 14:43:42 2006
@@ -16,6 +16,7 @@
 
 import java.util.List;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.runtime.RenderCommand;
 import org.apache.tapestry.runtime.RenderQueue;
@@ -29,16 +30,28 @@
 {
     private final List<RenderCommand> _queue = newList();
 
-    public void addFirst(RenderCommand command)
+    private final Log _log;
+
+    public RenderQueueImpl(Log log)
+    {
+        _log = log;
+    }
+
+    public void push(RenderCommand command)
     {
         _queue.add(command);
     }
 
     public void run(MarkupWriter writer)
     {
+        boolean debug = _log.isDebugEnabled();
+
         while (!_queue.isEmpty())
         {
             RenderCommand command = _queue.remove(_queue.size() - 1);
+
+            if (debug)
+                _log.debug("RenderCommand: " + command);
 
             command.render(writer, this);
         }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java Mon Aug 28 14:43:42 2006
@@ -18,6 +18,7 @@
 
 import javassist.CtClass;
 
+import org.apache.tapestry.Location;
 import org.apache.tapestry.Messages;
 import org.apache.tapestry.Resource;
 import org.apache.tapestry.internal.MessagesImpl;
@@ -110,5 +111,15 @@
     static String templateParseError(Resource resource, Throwable cause)
     {
         return MESSAGES.format("template-parse-error", resource, cause);
+    }
+
+    static String contentInsideBodyNotAllowed(Location location)
+    {
+        return MESSAGES.format("content-inside-body-not-allowed", location);
+    }
+    
+    static String mayNotNestElementsInsideBody(String elementName)
+    {
+        return MESSAGES.format("may-not-nest-elements-inside-body", elementName);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java Mon Aug 28 14:43:42 2006
@@ -21,11 +21,13 @@
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.Location;
 import org.apache.tapestry.Resource;
 import org.apache.tapestry.internal.LocationImpl;
 import org.apache.tapestry.internal.TapestryException;
 import org.apache.tapestry.internal.parser.AttributeToken;
+import org.apache.tapestry.internal.parser.BodyToken;
 import org.apache.tapestry.internal.parser.CDATAToken;
 import org.apache.tapestry.internal.parser.CommentToken;
 import org.apache.tapestry.internal.parser.ComponentTemplate;
@@ -80,13 +82,34 @@
 
     private boolean _textIsCData;
 
-    public TemplateParserImpl()
+    private boolean _insideBody;
+
+    private boolean _insideBodyErrorLogged;
+
+    private final Log _log;
+
+    public TemplateParserImpl(Log log)
     {
+        _log = log;
+
         _parserFactory = SAXParserFactory.newInstance();
 
         _parserFactory.setNamespaceAware(true);
     }
 
+    private void reset()
+    {
+        _tokens.clear();
+        _prefixResolutionStack.clear();
+        _templateResource = null;
+        _locator = null;
+        _textBuffer.setLength(0);
+        _textStartLocation = null;
+        _textIsCData = false;
+        _insideBody = false;
+        _insideBodyErrorLogged = false;
+    }
+
     public ComponentTemplate parseTemplate(Resource templateResource)
     {
         if (_parser == null)
@@ -99,7 +122,8 @@
             }
             catch (Exception ex)
             {
-                throw new RuntimeException(ServicesMessages.newParserError(templateResource, ex), ex);
+                throw new RuntimeException(ServicesMessages.newParserError(templateResource, ex),
+                        ex);
             }
         }
 
@@ -133,13 +157,7 @@
         }
         finally
         {
-            _tokens.clear();
-            _prefixResolutionStack.clear();
-            _templateResource = null;
-            _locator = null;
-            _textBuffer.setLength(0);
-            _textStartLocation = null;
-            _textIsCData = false;
+            reset();
         }
     }
 
@@ -174,6 +192,9 @@
     @Override
     public void characters(char[] ch, int start, int length) throws SAXException
     {
+        if (insideBody())
+            return;
+
         if (_textBuffer.length() == 0)
             _textStartLocation = getCurrentLocation();
 
@@ -184,7 +205,7 @@
         }
 
         // The parser will have converted XML entities into normal characters.
-        // What we want in the TexztToken is the raw characters to send to the
+        // What we want in the TextToken is the raw characters to send to the
         // client ... which means we have to revert those characters back
         // into XML entities.
 
@@ -232,6 +253,11 @@
     public void startElement(String uri, String localName, String qName, Attributes attributes)
             throws SAXException
     {
+        if (_insideBody)
+            throw new IllegalStateException(ServicesMessages
+                    .mayNotNestElementsInsideBody(localName));
+
+        // Add any accumulated text into a text token
         addTextToken();
 
         if (TAPESTRY_SCHEMA_5_0_0.equals(uri))
@@ -263,6 +289,28 @@
         }
     }
 
+    /**
+     * Checks to see if currently inside a t:body element (which should always be empty). Content is
+     * ignored inside a body. If inside a body, then a warning is logged (but only one warning per
+     * body element).
+     * 
+     * @return true if inside t:body, false otherwise
+     */
+    private boolean insideBody()
+    {
+        if (_insideBody)
+        {
+            // Limit to one logged error per infraction.
+
+            if (!_insideBodyErrorLogged)
+                _log.error(ServicesMessages.contentInsideBodyNotAllowed(getCurrentLocation()));
+
+            _insideBodyErrorLogged = true;
+        }
+
+        return _insideBody;
+    }
+
     private void startTapestryElement(String localName, Attributes attributes)
     {
         if (localName.equals("comp"))
@@ -271,6 +319,13 @@
             return;
         }
 
+        if (localName.equals("body"))
+        {
+            startBody();
+            return;
+        }
+
+        throw new IllegalArgumentException("Unknown localName: '" + localName + "'.");
     }
 
     private void startComponent(String localName, Attributes attributes)
@@ -306,17 +361,28 @@
         _tokens.addAll(attributeTokens);
     }
 
+    private void startBody()
+    {
+        _tokens.add(new BodyToken(getCurrentLocation()));
+
+        _insideBody = true;
+        _insideBodyErrorLogged = false;
+    }
+
     @Override
     public void endElement(String uri, String localName, String qName) throws SAXException
     {
         addTextToken();
 
-        // TODO: Handle tapestry namespace elements
+        // TODO: Handle tapestry namespace elements?
 
         // Because XML tags are always balanced, we don't even need to know what element just closed
         // when we assemble things later.
 
-        _tokens.add(new EndElementToken(getCurrentLocation()));
+        if (!_insideBody)
+            _tokens.add(new EndElementToken(getCurrentLocation()));
+
+        _insideBody = false;
     }
 
     private Location getCurrentLocation()
@@ -330,6 +396,9 @@
 
     public void comment(char[] ch, int start, int length) throws SAXException
     {
+        if (insideBody())
+            return;
+
         addTextToken();
 
         String comment = new String(ch, start, length);
@@ -368,6 +437,9 @@
 
     public void startCDATA() throws SAXException
     {
+        if (insideBody())
+            return;
+
         addTextToken();
 
         // Because CDATA doesn't mix with any other SAX/lexical events, we can simply turn on a flag
@@ -384,4 +456,12 @@
     public void startEntity(String name) throws SAXException
     {
     }
+
+    @Override
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
+    {
+
+    }
+    
+    
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/AttributePageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/AttributePageElement.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/AttributePageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/AttributePageElement.java Mon Aug 28 14:43:42 2006
@@ -39,4 +39,9 @@
         writer.attribute(_name, _value);
     }
 
+    @Override
+    public String toString()
+    {
+        return String.format("Attribute[%s=%s]", _name, _value);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java Mon Aug 28 14:43:42 2006
@@ -17,6 +17,7 @@
 import org.apache.tapestry.internal.InternalComponentResources;
 import org.apache.tapestry.internal.parser.AttributeToken;
 import org.apache.tapestry.runtime.RenderCommand;
+import org.apache.tapestry.runtime.RenderQueue;
 
 /**
  * Extended version of {@link org.apache.tapestry.internal.structure.PageElement} for elements that
@@ -61,4 +62,7 @@
 
     /** Adds a child component to its container. The child's id must be unique within the container. */
     void addChild(ComponentPageElement child);
+
+    /** Invoked when the component should render its body. */
+    void enqueueBeforeRenderBody(RenderQueue queue);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Mon Aug 28 14:43:42 2006
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry.internal.structure;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -35,6 +36,9 @@
  * {@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.
+ * <p>
+ * TODO: Possibly break this into two classes: the page element and the internal component resources
+ * implementation. For the moment, it works as a single class.
  * 
  * @author Howard M. Lewis Ship
  */
@@ -44,7 +48,12 @@
 
     private final String _id;
 
-    private List<PageElement> _template;
+    private String _completeId;
+
+    // For the momement, every component will have a template, even if it consists of
+    // just a page element to queue up a BeforeRenderBody phase.
+
+    private final List<PageElement> _template = newList();
 
     private List<PageElement> _body;
 
@@ -88,6 +97,48 @@
         _component = instantiator.newInstance(this);
     }
 
+    public String getCompleteId()
+    {
+        if (_completeId == null)
+            buildCompleteId();
+
+        return _completeId;
+    }
+
+    private void buildCompleteId()
+    {
+        List<String> ids = newList();
+
+        ComponentPageElement cpe = this;
+        while (true)
+        {
+            // Stop at the root component, which has no id.
+            String id = cpe.getId();
+
+            if (id == null)
+                break;
+
+            ids.add(id);
+
+            cpe = cpe.getContainer();
+        }
+
+        String pageName = getPage().getName();
+
+        Collections.reverse(ids);
+
+        StringBuffer buffer = new StringBuffer(pageName);
+
+        int count = ids.size();
+        for (int i = 0; i < count; i++)
+        {
+            buffer.append(i == 0 ? ':' : '.');
+            buffer.append(ids.get(i));
+        }
+
+        _completeId = buffer.toString();
+    }
+
     public Page getPage()
     {
         return _page;
@@ -108,9 +159,6 @@
 
     public void addToTemplate(PageElement element)
     {
-        if (_template == null)
-            _template = newList();
-
         _template.add(element);
     }
 
@@ -156,7 +204,14 @@
 
             _component.beforeRender(writer, event);
 
-            queue.addFirst(event.getResult() ? _renderTag : _afterRender);
+            if (event.getResult())
+                queue.push(_renderTag);
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("BeforeRender[%s]", getCompleteId());
         }
     };
 
@@ -170,12 +225,36 @@
             _component.renderTag(writer, event);
 
             if (event.getResult())
-            {
                 enqueueTemplate(queue);
-            }
-            else
-                queue.addFirst(_afterRender);
         }
+
+        @Override
+        public String toString()
+        {
+            return String.format("RenderTag[%s]", getCompleteId());
+        }
+
+    };
+
+    private RenderCommand _beforeRenderBody = new RenderCommand()
+    {
+        @SuppressNullCheck
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            LifecycleEvent<Boolean> event = newEvent(true);
+
+            _component.beforeRenderBody(writer, event);
+
+            if (event.getResult())
+                enqueueBody(queue);
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("BeforeRenderBody[%s]", getCompleteId());
+        }
+
     };
 
     private RenderCommand _renderCloseTag = new RenderCommand()
@@ -187,8 +266,16 @@
 
             _component.renderCloseTag(writer, event);
 
-            queue.addFirst(event.getResult() ? _renderTag : _afterRender);
+            if (event.getResult())
+                queue.push(_renderTag);
         }
+
+        @Override
+        public String toString()
+        {
+            return String.format("RenderCloseTag[%s]", getCompleteId());
+        }
+
     };
 
     private RenderCommand _afterRender = new RenderCommand()
@@ -198,16 +285,33 @@
         {
             LifecycleEvent<Boolean> event = newEvent(false);
 
-            _component.renderCloseTag(writer, event);
+            _component.afterRender(writer, event);
 
             if (event.getResult())
-                queue.addFirst(_beforeRender);
+            {
+                queue.push(this);
+                queue.push(_beforeRender);
+            }
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("AfterRender[%s]", getCompleteId());
         }
+
     };
 
+    /** Pushes AfterRender and BeforeRender phase commands onto the queue. */
     public void render(MarkupWriter writer, RenderQueue queue)
     {
-        queue.addFirst(_beforeRender);
+        queue.push(_afterRender);
+        queue.push(_beforeRender);
+    }
+
+    private void enqueueBody(RenderQueue queue)
+    {
+        pushElements(queue, _body);
     }
 
     private int size(List<?> list)
@@ -226,18 +330,31 @@
         // (including the render body directive), we want to
         // give the component a chance to render its close tag.
 
-        queue.addFirst(_renderCloseTag);
+        queue.push(_renderCloseTag);
 
         // Push them in reverse order, so that the first template item
         // it at the head of the queue.
 
-        int count = size(_template);
+        pushElements(queue, _template);
+    }
+
+    private void pushElements(RenderQueue queue, List<PageElement> list)
+    {
+        int count = size(list);
         for (int i = count - 1; i >= 0; i--)
-            queue.addFirst(_template.get(i));
+            queue.push(list.get(i));
+    }
 
-        // TODO: Component without template but with a body ... should probably push
-        // a _beforeRenderBody directly. That may be provided instead by the
-        // PageLoader.
+    public void enqueueBeforeRenderBody(RenderQueue queue)
+    {
+        if (_body != null)
+            queue.push(_beforeRenderBody);
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("ComponentResources[%s]", getCompleteId());
     }
 
 }

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=437852&r1=437851&r2=437852&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 Aug 28 14:43:42 2006
@@ -16,6 +16,7 @@
 
 import java.util.Locale;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.runtime.ComponentLifecycle;
 
 /**
@@ -46,10 +47,10 @@
      * Invoked during page construction time to connect the page's root component to the page
      * instance.
      */
-    void setRootElement(PageElement component);
+    void setRootElement(ComponentPageElement component);
 
     /** The root component of the page. */
-    PageElement getRootElement();
+    ComponentPageElement getRootElement();
 
     /**
      * Inform page that it is being detached from the current request. This occurs just before the
@@ -60,13 +61,13 @@
     void detached();
 
     /**
-     * Inform the page that it is attached to the current request. This occurs when a page is first referenced within
-     * a request. If the page was created for this request, the call to {@link #loaded()} will preceded the
-     * call to {@link #attached()}.
+     * Inform the page that it is attached to the current request. This occurs when a page is first
+     * referenced within a request. If the page was created for this request, the call to
+     * {@link #loaded()} will preceded the call to {@link #attached()}.
      */
-    
+
     void attached();
-    
+
     /**
      * Inform the page that it is now completely loaded.
      * 
@@ -81,4 +82,7 @@
      * notifications.
      */
     void addComponentLifecycle(ComponentLifecycle component);
+
+    /** Returns the log of the root element. */
+    Log getLog();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Mon Aug 28 14:43:42 2006
@@ -17,6 +17,7 @@
 import java.util.List;
 import java.util.Locale;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.runtime.ComponentLifecycle;
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
@@ -28,7 +29,7 @@
 
     private final Locale _locale;
 
-    private PageElement _rootElement;
+    private ComponentPageElement _rootElement;
 
     private final List<ComponentLifecycle> _components = newList();
 
@@ -48,12 +49,12 @@
         return _locale;
     }
 
-    public void setRootElement(PageElement component)
+    public void setRootElement(ComponentPageElement component)
     {
         _rootElement = component;
     }
 
-    public PageElement getRootElement()
+    public ComponentPageElement getRootElement()
     {
         return _rootElement;
     }
@@ -80,4 +81,8 @@
         // TODO: Define methods on ComponentLifecycle to invoke
     }
 
+    public Log getLog()
+    {
+        return _rootElement.getComponentModel().getLog();
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StartElementPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StartElementPageElement.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StartElementPageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StartElementPageElement.java Mon Aug 28 14:43:42 2006
@@ -33,4 +33,10 @@
     {
         writer.element(_name);
     }
+
+    @Override
+    public String toString()
+    {
+        return String.format("Start[%s]", _name);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/TextPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/TextPageElement.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/TextPageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/TextPageElement.java Mon Aug 28 14:43:42 2006
@@ -34,4 +34,10 @@
         writer.write(_text);
     }
 
+    @Override
+    public String toString()
+    {
+        return String.format("Text[%s]", _text);
+    }
+
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderCommand.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderCommand.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderCommand.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderCommand.java Mon Aug 28 14:43:42 2006
@@ -23,5 +23,5 @@
  */
 public interface RenderCommand
 {
-    void render(MarkupWriter writer, RenderQueue rendering);
+    void render(MarkupWriter writer, RenderQueue queue);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderQueue.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderQueue.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderQueue.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/RenderQueue.java Mon Aug 28 14:43:42 2006
@@ -22,5 +22,6 @@
  */
 public interface RenderQueue
 {
-    void addFirst(RenderCommand command);
+    /** Adds the new command to the front of the queue. */
+    void push(RenderCommand command);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java Mon Aug 28 14:43:42 2006
@@ -59,6 +59,12 @@
 
     /**
      * Signature for
+     * {@link org.apache.tapestry.runtime.ComponentLifecycle#beforeRenderBody(MarkupWriter, LifecycleEvent)}.
+     */
+    public static final MethodSignature BEFORE_RENDER_BODY_SIGNATURE = newLifecycleMethodSignature("beforeRenderBody");
+
+    /**
+     * Signature for
      * {@link org.apache.tapestry.runtime.ComponentLifecycle#renderCloseTag(MarkupWriter, LifecycleEvent)}
      */
     public static final MethodSignature RENDER_CLOSE_TAG_SIGNATURE = newLifecycleMethodSignature("renderCloseTag");

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties Mon Aug 28 14:43:42 2006
@@ -25,7 +25,10 @@
 field-already-claimed=Field %s of class %s is already claimed by %s and can not be claimed by %s.
 no-declared-method=Class %s does not declare method '%s'.
 incorrect-class-for-instantiator=Unable to create a component instantiator for class %s because class %s was provided instead.
-class-not-transformed=Class %s was not transformed for use as a component; either it does not have the @ComponentClass annotation, or was not in a package subject to component transformation.
+class-not-transformed=Class %s was not transformed for use as a component; \
+  either it does not have the @ComponentClass annotation, or was not in a package subject to component transformation.
 new-parser-error=Failure obtaining a SAX parser for resource %s: %s
 missing-template-resource=Template resource %s does not exist.
 template-parse-error=Failure parsing template %s: %s
+content-inside-body-not-allowed=Content inside a Tapestry body element is not allowed (at %s). The content has been ignored.
+may-not-nest-elements-inside-body=Element '%s' is nested within a Tapestry body element, which is not allowed.
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/tapestry_5_0_0.xsd
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/tapestry_5_0_0.xsd?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/tapestry_5_0_0.xsd (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/tapestry_5_0_0.xsd Mon Aug 28 14:43:42 2006
@@ -2,6 +2,7 @@
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"
     targetNamespace="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    <!-- comp is a component within the template -->
     <xs:element name="comp">
         <xs:complexType mixed="true">
             <xs:attribute name="id" type="xs:ID"/>
@@ -9,4 +10,6 @@
             <xs:anyAttribute/>
         </xs:complexType>
     </xs:element>
+    <!-- body identifies where the body of the component belongs within the component's template -->
+    <xs:element name="body"/>
 </xs:schema>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java Mon Aug 28 14:43:42 2006
@@ -24,14 +24,7 @@
     @RenderTag
     void renderMessage(MarkupWriter writer)
     {
-        writer.write("I Am HelloWorld");
-    }
-    
-    @RenderTag
-    void renderExtra(MarkupWriter writer)
-    {
-        writer.element("strong");
-        writer.write("Yow!");
-        writer.end();
+        writer.write("Bonjour from HelloWorld component.");
     }
+
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Strong.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Strong.java?rev=437852&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Strong.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Strong.java Mon Aug 28 14:43:42 2006
@@ -0,0 +1,36 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.components;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.RenderCloseTag;
+import org.apache.tapestry.annotations.RenderTag;
+
+@ComponentClass
+public class Strong
+{
+    @RenderTag
+    public void start(MarkupWriter writer)
+    {
+        writer.element("strong");
+    }
+
+    @RenderCloseTag
+    public void end(MarkupWriter writer)
+    {
+        writer.end();
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java Mon Aug 28 14:43:42 2006
@@ -17,12 +17,9 @@
 import org.apache.tapestry.Location;
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.internal.parser.AttributeToken;
-import org.apache.tapestry.internal.parser.EndElementToken;
 import org.apache.tapestry.internal.parser.StartElementToken;
 import org.apache.tapestry.internal.parser.TextToken;
-import org.apache.tapestry.internal.services.MarkupWriterImpl;
-import org.apache.tapestry.internal.services.PageElementFactory;
-import org.apache.tapestry.internal.services.PageElementFactoryImpl;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.PageElement;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.runtime.RenderQueue;
@@ -54,6 +51,7 @@
 
         verify();
 
+        assertEquals(element.toString(), "Start[fred]");
         assertEquals(writer.toString(), "<fred/>");
     }
 
@@ -79,6 +77,7 @@
 
         verify();
 
+        assertEquals(element.toString(), "Attribute[name=value]");
         assertEquals(writer.toString(), "<root name=\"value\"/>");
     }
 
@@ -88,15 +87,13 @@
         ComponentInstantiatorSource source = newComponentInstantiatorSource();
         ComponentClassResolver resolver = newComponentClassResolver();
         MarkupWriter writer = new MarkupWriterImpl();
-        Location l = newLocation();
         RenderQueue queue = newRenderQueue();
 
         replay();
 
         PageElementFactory factory = new PageElementFactoryImpl(source, resolver);
-        EndElementToken token = new EndElementToken(l);
 
-        PageElement element = factory.newEndElement(token);
+        PageElement element = factory.newEndElement();
 
         writer.element("root");
         writer.write("before");
@@ -108,6 +105,7 @@
 
         verify();
 
+        assertEquals(element.toString(), "End");
         assertEquals(writer.toString(), "<root>before<nested/>after</root>");
     }
 
@@ -116,15 +114,13 @@
     {
         ComponentInstantiatorSource source = newComponentInstantiatorSource();
         ComponentClassResolver resolver = newComponentClassResolver();
-        Location l1 = newLocation();
-        Location l2 = newLocation();
 
         replay();
 
         PageElementFactory factory = new PageElementFactoryImpl(source, resolver);
 
-        PageElement element1 = factory.newEndElement(new EndElementToken(l1));
-        PageElement element2 = factory.newEndElement(new EndElementToken(l2));
+        PageElement element1 = factory.newEndElement();
+        PageElement element2 = factory.newEndElement();
 
         assertSame(element2, element1);
 
@@ -152,6 +148,29 @@
 
         verify();
 
+        assertEquals(element.toString(), "Text[some text]");
         assertEquals(writer.toString(), "<root>some text</root>");
+    }
+
+    @Test
+    public void render_body_element()
+    {
+        ComponentInstantiatorSource source = newComponentInstantiatorSource();
+        ComponentClassResolver resolver = newComponentClassResolver();
+        RenderQueue queue = newRenderQueue();
+        ComponentPageElement component = newMock(ComponentPageElement.class);
+        MarkupWriter writer = newMock(MarkupWriter.class);
+
+        component.enqueueBeforeRenderBody(queue);
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, resolver);
+
+        PageElement element = factory.newRenderBodyElement(component);
+
+        element.render(writer, queue);
+
+        verify();
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/TemplateParserImplTest.java Mon Aug 28 14:43:42 2006
@@ -16,23 +16,27 @@
 
 import java.util.List;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.Locatable;
 import org.apache.tapestry.Location;
 import org.apache.tapestry.Resource;
 import org.apache.tapestry.internal.ClasspathResource;
+import org.apache.tapestry.internal.TapestryException;
 import org.apache.tapestry.internal.parser.AttributeToken;
+import org.apache.tapestry.internal.parser.BodyToken;
 import org.apache.tapestry.internal.parser.CDATAToken;
 import org.apache.tapestry.internal.parser.CommentToken;
 import org.apache.tapestry.internal.parser.ComponentTemplate;
+import org.apache.tapestry.internal.parser.EndElementToken;
 import org.apache.tapestry.internal.parser.StartComponentToken;
 import org.apache.tapestry.internal.parser.StartElementToken;
 import org.apache.tapestry.internal.parser.TemplateToken;
 import org.apache.tapestry.internal.parser.TextToken;
 import org.apache.tapestry.test.BaseTestCase;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import static org.easymock.EasyMock.contains;
+
 /**
  * This is used to test the template parser ... and in some cases, the underlying behavior of the
  * SAX APIs.
@@ -41,37 +45,32 @@
  * 
  * @author Howard M. Lewis Ship
  */
-@Test(sequential = true)
 public class TemplateParserImplTest extends BaseTestCase
 {
-    private TemplateParser _parser;
 
-    /**
-     * What's nice is that we get to set up a single parser for all the test methods to share, which
-     * will actually do a very good job of simulating how a parser is used in production.
-     */
-    @BeforeClass
-    public void setup_parser()
+    private TemplateParser newParser(Log log)
     {
-        _parser = new TemplateParserImpl();
+        return new TemplateParserImpl(log);
     }
 
-    @AfterClass
-    public void discard_parser()
-    {
-        _parser = null;
-    }
-
-    private ComponentTemplate parse(String file)
+    private synchronized ComponentTemplate parse(Log log, String file)
     {
         Resource resource = getResource(file);
 
-        return _parser.parseTemplate(resource);
+        return newParser(log).parseTemplate(resource);
     }
 
     private List<TemplateToken> tokens(String file)
     {
-        return parse(file).getTokens();
+        Log log = newLog();
+
+        replay();
+
+        List<TemplateToken> tokens = parse(log, file).getTokens();
+
+        verify();
+
+        return tokens;
     }
 
     private Resource getResource(String file)
@@ -99,10 +98,14 @@
     }
 
     @Test
-    void just_HTML()
+    synchronized void just_HTML()
     {
+        Log log = newLog();
         Resource resource = getResource("justHTML.html");
-        ComponentTemplate template = _parser.parseTemplate(resource);
+
+        replay();
+
+        ComponentTemplate template = newParser(log).parseTemplate(resource);
 
         assertSame(template.getResource(), resource);
 
@@ -146,6 +149,8 @@
 
         // Line number is the *start* line of the whole text block.
         checkLine(t14, 6);
+
+        verify();
     }
 
     @Test
@@ -237,6 +242,53 @@
     }
 
     @Test
+    void body_element() throws Exception
+    {
+        List<TemplateToken> tokens = tokens("body_element.html");
+
+        // start(html), text, body, text, end(html)
+        assertEquals(tokens.size(), 5);
+
+        assertTrue(get(tokens, 2) instanceof BodyToken);
+    }
+
+    @Test
+    void illegal_nesting_within_body_element() throws Exception
+    {
+        try
+        {
+            tokens("illegal_nesting_within_body_element.html");
+            unreachable();
+        }
+        catch (TapestryException ex)
+        {
+            assertTrue(ex.getMessage().contains(
+                    "Element 'xyz' is nested within a Tapestry body element"));
+            assertEquals(ex.getLocation().getLine(), 2);
+        }
+    }
+
+    @Test
+    void content_within_body_element() throws Exception
+    {
+        Log log = newLog();
+
+        log.error(contains("Content inside a Tapestry body element is not allowed"));
+
+        replay();
+
+        List<TemplateToken> tokens = parse(log, "content_within_body_element.html").getTokens();
+
+        assertEquals(tokens.size(), 5);
+
+        assertTrue(get(tokens, 2) instanceof BodyToken);
+        assertTrue(get(tokens, 3) instanceof TextToken);
+        assertTrue(get(tokens, 4) instanceof EndElementToken);
+
+        verify();
+    }
+
+    @Test
     void component_with_parameters() throws Exception
     {
         List<TemplateToken> tokens = tokens("componentWithParameters.html");
@@ -249,7 +301,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?
+        // alphabetically? 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/structure/PageImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java Mon Aug 28 14:43:42 2006
@@ -32,7 +32,7 @@
     @Test
     public void accessor_methods()
     {
-        PageElement root = newMock(PageElement.class);
+        ComponentPageElement root = newMock(ComponentPageElement.class);
 
         replay();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Start.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Start.html?rev=437852&r1=437851&r2=437852&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Start.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Start.html Mon Aug 28 14:43:42 2006
@@ -1,11 +1,13 @@
-<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">    
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
     <head>
         <title>First Tapestry 5 Page</title>
     </head>
-    <body> This is the <span style="color:green">First Tapestry 5 Page, ever!</span>. 
-    
-        <p>
-            Output from HelloWorld component: 
-                <t:comp id="foo" type="HelloWorld"/>                
-        </p></body>
+    <body>
+        <p> This is the <span style="font-style: bold; color: blue;">First Tapestry 5 Page,
+            ever!</span>. </p>
+        <p> Here's some output from the HelloWorld component: <t:comp id="strong" type="Strong">
+                <t:comp id="hello" type="HelloWorld"/>
+            </t:comp>
+        </p>
+    </body>
 </html>

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/body_element.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/body_element.html?rev=437852&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/body_element.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/body_element.html Mon Aug 28 14:43:42 2006
@@ -0,0 +1,5 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+Before
+<t:body/>
+After
+</html>
\ No newline at end of file

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/content_within_body_element.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/content_within_body_element.html?rev=437852&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/content_within_body_element.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/content_within_body_element.html Mon Aug 28 14:43:42 2006
@@ -0,0 +1,7 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    <t:body>
+        Some text (starts on line 2)
+        <!-- A comment -->
+        <![CDATA[ CData text ]]>
+    </t:body>
+</html>

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/illegal_nesting_within_body_element.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/illegal_nesting_within_body_element.html?rev=437852&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/illegal_nesting_within_body_element.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/illegal_nesting_within_body_element.html Mon Aug 28 14:43:42 2006
@@ -0,0 +1,3 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    <t:body><xyz/></t:body>
+</html>