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 2009/04/18 00:22:42 UTC

svn commit: r766168 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/services/ test/java/org/apache/tapestry5/internal/services/ test/resources/org/apache/tapestry5/internal/services/

Author: hlship
Date: Fri Apr 17 22:22:41 2009
New Revision: 766168

URL: http://svn.apache.org/viewvc?rev=766168&view=rev
Log:
TAP5-571: Misplaced <t:parameter> results in an inscrutable exception

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/internal/services/misplaced_parameter.tml
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateParserState.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TemplateParserImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java?rev=766168&r1=766167&r2=766168&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java Fri Apr 17 22:22:41 2009
@@ -314,50 +314,7 @@
 
     /**
      * Processes an element through to its matching end tag.
-     */
-    void element(TemplateParserState initialState) throws XMLStreamException
-    {
-        TemplateParserState state = setupForElement(initialState);
-
-        if (!processStartElement(state)) return;
-
-        // Now start working through the body of the element, recursively.
-
-        while (active)
-        {
-            switch (reader.next())
-            {
-                case START_ELEMENT:
-
-                    // The recursive part: when we see a new element start.
-
-                    element(state);
-                    break;
-
-                case END_ELEMENT:
-
-                    // At the end of an element, we're done and can return.
-                    // This is the matching end element for the start element
-                    // that invoked this method.
-
-                    endElement(state);
-
-                    return;
-
-                default:
-                    textContent(state);
-            }
-        }
-    }
-
-    private TemplateParserState setupForElement(TemplateParserState initialState)
-    {
-        processTextBuffer(initialState);
-
-        return checkForXMLSpaceAttribute(initialState);
-    }
-
-    /**
+     * <p/>
      * An element can be:
      * <p/>
      * a Tapestry component via &lt;t:type&gt;
@@ -379,12 +336,11 @@
      * The body &lt;t:body&gt;
      * <p/>
      * An ordinary element
-     *
-     * @return true if processing of the elements body should continue normally, or false if the elements body (and end
-     *         tag) have already been consumed
      */
-    private boolean processStartElement(TemplateParserState state) throws XMLStreamException
+    void element(TemplateParserState initialState) throws XMLStreamException
     {
+        TemplateParserState state = setupForElement(initialState);
+
         String uri = reader.getNamespaceURI();
         String name = reader.getLocalName();
 
@@ -395,21 +351,21 @@
             {
                 removeContent();
 
-                return false;
+                return;
             }
 
             if (name.equalsIgnoreCase("content"))
             {
                 limitContent(state);
 
-                return false;
+                return;
             }
 
             if (name.equalsIgnoreCase("extension-point"))
             {
                 extensionPoint(state);
 
-                return false;
+                return;
             }
 
             if (name.equalsIgnoreCase("replace"))
@@ -427,7 +383,7 @@
             if (name.equalsIgnoreCase("body"))
             {
                 body();
-                return false;
+                return;
             }
 
             if (name.equalsIgnoreCase("container"))
@@ -438,43 +394,82 @@
             if (name.equalsIgnoreCase("block"))
             {
                 block(state);
-                return false;
+                return;
             }
 
             if (name.equalsIgnoreCase("parameter"))
             {
-                classicParameter();
+                classicParameter(state);
 
-                // Default handling for the body of the parameter is acceptible.
-                return true;
+                return;
             }
 
-            possibleTapestryComponent(null, reader.getLocalName().replace('.', '/'));
+            possibleTapestryComponent(state, null, reader.getLocalName().replace('.', '/'));
 
-            return true;
+            return;
         }
 
         if (uri != null && uri.startsWith(LIB_NAMESPACE_URI_PREFIX))
         {
-            libraryNamespaceComponent();
+            libraryNamespaceComponent(state);
 
-            return true;
+            return;
         }
 
         if (TAPESTRY_PARAMETERS_URI.equals(uri))
         {
-            parameterElement();
+            parameterElement(state);
 
-            return true;
+            return;
         }
 
         // Just an ordinary element ... unless it has t:id or t:type
 
-        possibleTapestryComponent(reader.getLocalName(), null);
+        possibleTapestryComponent(state, reader.getLocalName(), null);
+    }
+
+    /**
+     * Processes a body of an element including text and (recursively) nested elements. Adds an {@link
+     * org.apache.tapestry5.internal.parser.TokenType#END_ELEMENT} token before returning.
+     *
+     * @param state
+     * @throws XMLStreamException
+     */
+    private void processBody(TemplateParserState state)
+            throws XMLStreamException
+    {
+        while (active)
+        {
+            switch (reader.next())
+            {
+                case START_ELEMENT:
+
+                    // The recursive part: when we see a new element start.
+
+                    element(state);
+                    break;
+
+                case END_ELEMENT:
+
+                    // At the end of an element, we're done and can return.
+                    // This is the matching end element for the start element
+                    // that invoked this method.
+
+                    endElement(state);
+
+                    return;
+
+                default:
+                    textContent(state);
+            }
+        }
+    }
 
-        // Let element() take it from here (body plus end element token).
+    private TemplateParserState setupForElement(TemplateParserState initialState)
+    {
+        processTextBuffer(initialState);
 
-        return true;
+        return checkForXMLSpaceAttribute(initialState);
     }
 
     /**
@@ -492,7 +487,7 @@
 
         tokenAccumulator.add(new ExtensionPointToken(id, getLocation()));
 
-        addContentToOverride(state, id);
+        addContentToOverride(state.insideComponent(false), id);
     }
 
     private String getRequiredIdAttribute()
@@ -561,7 +556,7 @@
                     "The <content> element may not be nested within another <content> element.");
 
 
-        TemplateParserState newState = state.collectingContent();
+        TemplateParserState newState = state.collectingContent().insideComponent(false);
 
         // Clear out any tokens that precede the <t:content> element
 
@@ -637,7 +632,7 @@
     /**
      * Added in release 5.1.
      */
-    private void libraryNamespaceComponent()
+    private void libraryNamespaceComponent(TemplateParserState state) throws XMLStreamException
     {
         String uri = reader.getNamespaceURI();
 
@@ -648,14 +643,15 @@
         if (!LIBRARY_PATH_PATTERN.matcher(path).matches())
             throw new RuntimeException(ServicesMessages.invalidPathForLibraryNamespace(uri));
 
-        possibleTapestryComponent(null, path + "/" + reader.getLocalName());
+        possibleTapestryComponent(state, null, path + "/" + reader.getLocalName());
     }
 
     /**
      * @param elementName
      * @param identifiedType the type of the element, usually null, but may be the component type derived from element
      */
-    private void possibleTapestryComponent(String elementName, String identifiedType)
+    private void possibleTapestryComponent(TemplateParserState state, String elementName, String identifiedType)
+            throws XMLStreamException
     {
         String id = null;
         String type = identifiedType;
@@ -736,6 +732,8 @@
 
         if (id != null)
             componentIds.put(id, location);
+
+        processBody(state.insideComponent(isComponent));
     }
 
     private void addDefineNamespaceTokens()
@@ -789,26 +787,40 @@
      * Handler for Tapestry 5.0's "classic" &lt;t:parameter&gt; element. This turns into a {@link
      * org.apache.tapestry5.internal.parser.ParameterToken} and the body and end element are provided normally.
      */
-    private void classicParameter()
+    private void classicParameter(TemplateParserState state) throws XMLStreamException
     {
         String parameterName = getSingleParameter("name");
 
         if (InternalUtils.isBlank(parameterName))
             throw new TapestryException(ServicesMessages.parameterElementNameRequired(), getLocation(), null);
 
+        ensureParameterWithinComponent(state);
+
         tokenAccumulator.add(new ParameterToken(parameterName, getLocation()));
+
+        processBody(state.insideComponent(false));
+    }
+
+    private void ensureParameterWithinComponent(TemplateParserState state)
+    {
+        if (!state.isInsideComponent())
+            throw new RuntimeException("Block parameters are only allowed directly within component elements.");
     }
 
     /**
      * Tapestry 5.1 uses a special namespace (usually mapped to "p:") and the name becomes the parameter element.
      */
-    private void parameterElement()
+    private void parameterElement(TemplateParserState state) throws XMLStreamException
     {
+        ensureParameterWithinComponent(state);
+
         if (reader.getAttributeCount() > 0)
             throw new TapestryException(ServicesMessages.parameterElementDoesNotAllowAttributes(), getLocation(),
                                         null);
 
         tokenAccumulator.add(new ParameterToken(reader.getLocalName(), getLocation()));
+
+        processBody(state.insideComponent(false));
     }
 
 
@@ -874,23 +886,7 @@
 
         tokenAccumulator.add(new BlockToken(blockId, getLocation()));
 
-        while (active)
-        {
-            switch (reader.next())
-            {
-                case START_ELEMENT:
-                    element(state);
-                    break;
-
-                case END_ELEMENT:
-                    endElement(state);
-                    return;
-
-                default:
-                    textContent(state);
-            }
-        }
-
+        processBody(state.insideComponent(false));
     }
 
     private String getSingleParameter(String attributeName)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateParserState.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateParserState.java?rev=766168&r1=766167&r2=766168&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateParserState.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateParserState.java Fri Apr 17 22:22:41 2009
@@ -26,26 +26,40 @@
 
     private final boolean collectingContent;
 
+    private final boolean insideComponent;
+
     TemplateParserState()
     {
         compressWhitespace = false;
         collectingContent = false;
+        insideComponent = false;
     }
 
-    private TemplateParserState(boolean compressWhitespace, boolean collectingContent)
+    private TemplateParserState(boolean compressWhitespace, boolean collectingContent, boolean insideComponent)
     {
         this.compressWhitespace = compressWhitespace;
         this.collectingContent = collectingContent;
+        this.insideComponent = insideComponent;
     }
 
     TemplateParserState compressWhitespace(boolean flag)
     {
-        return new TemplateParserState(flag, collectingContent);
+        return flag == compressWhitespace ? this : new TemplateParserState(flag, collectingContent, insideComponent);
     }
 
     TemplateParserState collectingContent()
     {
-        return new TemplateParserState(compressWhitespace, true);
+        return collectingContent ? this : new TemplateParserState(compressWhitespace, true, insideComponent);
+    }
+
+    TemplateParserState insideComponent(boolean flag)
+    {
+        return flag == insideComponent ? this : new TemplateParserState(compressWhitespace, collectingContent, flag);
+    }
+
+    boolean isInsideComponent()
+    {
+        return insideComponent;
     }
 
     boolean isCompressWhitespace()
@@ -64,7 +78,7 @@
     @Override
     public String toString()
     {
-        return String.format("TemplateParserState[compressWhitespace=%s, collectingContent=%s]", compressWhitespace,
-                             collectingContent);
+        return String.format("TemplateParserState[compressWhitespace=%s, collectingContent=%s, insideComponent=%s]",
+                             compressWhitespace, collectingContent, insideComponent);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TemplateParserImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TemplateParserImplTest.java?rev=766168&r1=766167&r2=766168&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TemplateParserImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TemplateParserImplTest.java Fri Apr 17 22:22:41 2009
@@ -694,7 +694,11 @@
 
                 { "missing_id_in_replace_element.tml", "The <replace> element must have an id attribute.", 3 },
 
-                { "extension_point_must_have_id.tml", "The <extension-point> element must have an id attribute.", 3 }
+                { "extension_point_must_have_id.tml", "The <extension-point> element must have an id attribute.", 3 },
+
+                { "misplaced_parameter.tml", "Block parameters are only allowed directly within component elements.",
+                        5 },
+
         };
     }
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/internal/services/misplaced_parameter.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/internal/services/misplaced_parameter.tml?rev=766168&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/internal/services/misplaced_parameter.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/internal/services/misplaced_parameter.tml Fri Apr 17 22:22:41 2009
@@ -0,0 +1,9 @@
+<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
+             xmlns:p="tapestry:parameter">
+
+    <not-a-component>
+        <p:foo/>
+    </not-a-component>
+
+</t:container>
+