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 2007/01/12 02:57:40 UTC
svn commit: r495465 [1/2] - in /tapestry/tapestry5/tapestry-core/trunk/src:
main/java/org/apache/tapestry/ main/java/org/apache/tapestry/internal/parser/
main/java/org/apache/tapestry/internal/services/
main/java/org/apache/tapestry/internal/structure/...
Author: hlship
Date: Thu Jan 11 17:57:38 2007
New Revision: 495465
URL: http://svn.apache.org/viewvc?view=rev&rev=495465
Log:
Rework the PageLoader to property support multiple threads.
Begin work on adding support for <t:block> and <t:parameter> in templates.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Block.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BlockToken.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/ParameterToken.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BlockImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BodyPageElement.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/BlockImplTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/block_element.html
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_blank.html
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_omitted.html
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/parameter_element.html
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/unexpected_attribute_in_block_element.html
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/unexpected_attribute_in_parameter_element.html
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/unexpected_element_in_tapestry_namespace.html
Modified:
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/TokenType.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/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/ComponentPageElement.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/internal/services/TemplateParserImplTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterConflict.html
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Block.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Block.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Block.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Block.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,26 @@
+// Copyright 2007 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;
+
+/**
+ * A block is a collection of static text and elements, and components, derived from a component
+ * template. In the template, a block is demarcated using the <t:block> or <t:parameter>
+ * elements. The interface defines no methods, but the provided implementations of Block are capable
+ * of rendering their contents on demand.
+ */
+public interface Block
+{
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BlockToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BlockToken.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BlockToken.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/BlockToken.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,51 @@
+// Copyright 2007 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.ioc.Location;
+
+/**
+ * A block, used to enclose a chunk of template (including components) and control when or if the
+ * content is rendered.
+ */
+public class BlockToken extends TemplateToken
+{
+ private final String _id;
+
+ /**
+ * @param id
+ * the id of the block, or null for an annonymous block
+ * @param location
+ * of the block element
+ */
+ public BlockToken(String id, Location location)
+ {
+ super(TokenType.BLOCK, location);
+
+ _id = id;
+ }
+
+ /** Returns the block's template-unique id, or null if the block element did not specify an id. */
+ public String getId()
+ {
+ return _id;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("Block[%s]", _id == null ? "" : _id);
+ }
+}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -17,7 +17,7 @@
import org.apache.tapestry.ioc.Location;
/**
- * Ends a previously started element (including components, parameters, etc.).
+ * Ends a previously started element (including components, parameters, blocks, etc.).
*/
public class EndElementToken extends TemplateToken
{
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/ParameterToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/ParameterToken.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/ParameterToken.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/ParameterToken.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,47 @@
+// Copyright 2007 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.ioc.Location;
+
+/** A parameter block to be passed to a component as a parameter. */
+public class ParameterToken extends TemplateToken
+{
+ private final String _name;
+
+ /**
+ * @param name
+ * the name of the parameter to be bound
+ * @param location
+ * location of the element
+ */
+ public ParameterToken(String name, Location location)
+ {
+ super(TokenType.PARAMETER, location);
+
+ _name = name;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("Parameter[%s]", _name);
+ }
+}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -12,15 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.internal.parser;
-
-/**
- * Defines the different types of {@link org.apache.tapestry.internal.parser.TemplateToken}s. Each
- * value maps to a particular subclass of TemplateToken.
- *
- *
- */
-public enum TokenType {
-
- ATTRIBUTE, CDATA, COMMENT, END_ELEMENT, START_COMPONENT, START_ELEMENT, TEXT, BODY, EXPANSION
-}
+package org.apache.tapestry.internal.parser;
+
+/**
+ * Defines the different types of {@link org.apache.tapestry.internal.parser.TemplateToken}s. Each
+ * value maps to a particular subclass of TemplateToken.
+ */
+public enum TokenType {
+
+ ATTRIBUTE, CDATA, COMMENT, END_ELEMENT, START_COMPONENT, START_ELEMENT, TEXT, BODY, EXPANSION, PARAMETER, BLOCK
+}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -14,36 +14,11 @@
package org.apache.tapestry.internal.services;
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
-
-import java.util.List;
import java.util.Locale;
-import java.util.Set;
-import org.apache.commons.logging.Log;
-import org.apache.tapestry.Binding;
-import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.events.InvalidationListener;
-import org.apache.tapestry.internal.InternalConstants;
import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
-import org.apache.tapestry.internal.parser.AttributeToken;
-import org.apache.tapestry.internal.parser.CommentToken;
-import org.apache.tapestry.internal.parser.ComponentTemplate;
-import org.apache.tapestry.internal.parser.ExpansionToken;
-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.internal.structure.ComponentPageElement;
import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.internal.structure.PageElement;
-import org.apache.tapestry.internal.structure.PageImpl;
-import org.apache.tapestry.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry.ioc.internal.util.IdAllocator;
-import org.apache.tapestry.ioc.internal.util.InternalUtils;
-import org.apache.tapestry.ioc.internal.util.TapestryException;
-import org.apache.tapestry.model.ComponentModel;
-import org.apache.tapestry.model.EmbeddedComponentModel;
import org.apache.tapestry.services.BindingSource;
import org.apache.tapestry.services.PersistentFieldManager;
@@ -60,10 +35,6 @@
private final PersistentFieldManager _persistentFieldManager;
- private Page _page;
-
- private Locale _locale;
-
public PageLoaderImpl(ComponentTemplateSource templateSource,
PageElementFactory pageElementFactory, BindingSource bindingSource,
LinkFactory linkFactory, PersistentFieldManager persistentFieldManager)
@@ -76,419 +47,19 @@
}
/**
- * Stack of elements within the current template, used to determine on which object to invoke
- * {@link ComponentPageElement#addToBody(PageElement)}. Each new element (component or
- * otherwise) will push a value onto this stack, each end element will pop a value off the
- * stack.
- */
- private final List<ComponentPageElement> _elementStack = newList();
-
- private final List<ComponentPageElement> _componentQueue = newList();
-
- /**
* For the moment, this service is a singleton. However, only a single page can be built at one
- * time. The coming rework will shift the local variables to a secondary process object and
+ * time. The coming rework will shift the loc al variables to a secondary process object and
* allow the loader to work in parallel.
*/
- public synchronized Page loadPage(String pageClassName, Locale locale)
- {
- try
- {
- _locale = locale;
-
- _page = new PageImpl(pageClassName, _locale, _linkFactory, _persistentFieldManager);
-
- loadRootComponent(pageClassName);
-
- workComponentQueue();
-
- // The page is *loaded* before it is attached to the request.
- // This is to help ensure that no client-specific information leaks
- // into the page.
-
- _page.loaded();
-
- return _page;
- }
- finally
- {
- _page = null;
- _locale = null;
- _elementStack.clear();
- _componentQueue.clear();
- }
- }
-
- private void loadRootComponent(String className)
- {
- ComponentPageElement rootComponent = _pageElementFactory.newRootComponentElement(
- _page,
- className);
-
- _page.setRootElement(rootComponent);
-
- push(_componentQueue, rootComponent);
- }
-
- /** Works the component queue, until exausted. */
- private void workComponentQueue()
- {
- while (!_componentQueue.isEmpty())
- {
- ComponentPageElement componentElement = pop(_componentQueue);
-
- loadTemplateForComponent(componentElement);
- }
- }
-
- /**
- * Do you smell something? I'm smelling that this class needs to be redesigned to not need a
- * central method this large and hard to test. I think a lot of instance and local variables
- * need to be bundled up into some kind of process object. This code is effectively too big to
- * be tested except through integration testing.
- */
- private void loadTemplateForComponent(ComponentPageElement loadingElement)
+ public Page loadPage(String pageClassName, Locale locale)
{
- ComponentModel model = loadingElement.getComponentResources().getComponentModel();
-
- String componentClassName = model.getComponentClassName();
- ComponentTemplate template = _templateSource.getTemplate(model, _locale);
-
- // TODO: This needs some work, because the component may have defined embedded components
- // that we need to log errors about.
-
- // When the template for a component is missing, we pretend it consists of just a RenderBody
- // phase. Missing is not an error ... many component simply do not have a template.
-
- if (template.isMissing())
- {
- addRenderBodyElement(loadingElement);
- return;
- }
-
- // Pre-allocate ids to avoid later name collisions.
-
- Log log = model.getLog();
-
- Set<String> embeddedIds = CollectionFactory.newSet(model.getEmbeddedComponentIds());
-
- IdAllocator idAllocator = new IdAllocator();
- for (String id : template.getComponentIds())
- {
- idAllocator.allocateId(id);
- embeddedIds.remove(id);
- }
-
- if (!embeddedIds.isEmpty())
- log.error(ServicesMessages.embeddedComponentsNotInTemplate(
- embeddedIds,
- componentClassName));
-
- // 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
- // is stuff from its container's template.
-
- PageElement activePageElement = loadingElement;
- ComponentPageElement activeComponent = loadingElement;
- List<PageElement> activePageElementStack = newList();
- List<ComponentPageElement> activeComponentStack = newList();
-
- boolean directlyInsideSubcomponent = false;
-
- for (TemplateToken token : template.getTokens())
- {
- switch (token.getTokenType())
- {
- // case CDATA: -- not yet supported
-
- case COMMENT:
- add(loadingElement, activeComponent, _pageElementFactory
- .newCommentElement((CommentToken) token));
- break;
-
- case TEXT:
-
- add(loadingElement, activeComponent, _pageElementFactory
- .newTextElement((TextToken) token));
- break;
-
- case EXPANSION:
-
- add(loadingElement, activeComponent, _pageElementFactory.newExpansionElement(
- loadingElement.getComponentResources(),
- (ExpansionToken) token));
- break;
-
- case START_ELEMENT:
-
- PageElement newElement = _pageElementFactory
- .newStartElement((StartElementToken) token);
-
- add(loadingElement, 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);
-
- activePageElement = newElement;
-
- // Any additional attribute tokens should be directed to
- // the activePageElement, and not be directed to
- // the activeComponent.
-
- directlyInsideSubcomponent = false;
-
- break;
-
- case BODY:
-
- addRenderBodyElement(loadingElement);
-
- // BODY tokens are *not* matched by END_ELEMENT tokens. Nor will there be
- // text or comment content "inside" the BODY.
-
- break;
-
- case END_ELEMENT:
-
- // 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 == loadingElement)
- add(loadingElement, activeComponent, _pageElementFactory.newEndElement());
-
- activePageElement = pop(activePageElementStack);
- activeComponent = pop(activeComponentStack);
-
- break;
-
- case START_COMPONENT:
-
- // The container for any components is the loading component, regardless of
- // how the component elements are nested within the loading component's
- // template.
-
- StartComponentToken startComponent = (StartComponentToken) token;
-
- String elementName = startComponent.getElementName();
+ // For the moment, the processors are used once and discarded. Perhaps it is worth the
+ // effort to pool them for reuse, but not too likely.
- String embeddedType = startComponent.getType();
+ PageLoaderProcessor processor = new PageLoaderProcessor(_templateSource,
+ _pageElementFactory, _bindingSource, _linkFactory, _persistentFieldManager);
- String embeddedId = startComponent.getId();
-
- String embeddedComponentClassName = null;
-
- // We know that if embeddedId is null, embeddedType is not.
-
- if (embeddedId == null)
- embeddedId = generateEmbeddedId(embeddedType, idAllocator);
-
- EmbeddedComponentModel embeddedModel = model
- .getEmbeddedComponentModel(embeddedId);
-
- if (embeddedModel != null)
- {
- String modelType = embeddedModel.getComponentType();
-
- if (InternalUtils.isNonBlank(modelType) && embeddedType != null)
- log.error(ServicesMessages.compTypeConflict(
- embeddedId,
- embeddedType,
- modelType));
-
- embeddedType = modelType;
- embeddedComponentClassName = embeddedModel.getComponentClassName();
- }
-
- if (InternalUtils.isBlank(embeddedType)
- && InternalUtils.isBlank(embeddedComponentClassName))
- {
- if (elementName != null)
- embeddedType = "Any";
- else
- throw new TapestryException(ServicesMessages
- .noTypeForEmbeddedComponent(embeddedId, componentClassName),
- token, null);
- }
-
- ComponentPageElement newComponent = _pageElementFactory.newComponentElement(
- _page,
- loadingElement,
- embeddedId,
- embeddedType,
- embeddedComponentClassName,
- elementName,
- startComponent.getLocation());
-
- addMixinsToComponent(newComponent, embeddedModel, startComponent.getMixins());
-
- add(loadingElement, activeComponent, newComponent);
-
- if (embeddedModel != null)
- addParametersFromModel(embeddedModel, loadingElement, newComponent);
-
- // Remember that we have to load this new component and any of its
- // subcomponents, later.
-
- push(_componentQueue, newComponent);
-
- push(activePageElementStack, activePageElement);
- push(activeComponentStack, activeComponent);
-
- activePageElement = newComponent;
- activeComponent = newComponent;
-
- // If we see any attribute tokens immediately following the start
- // of a component, they are parameters of the component.
- // Remember that attribute token can only directly follow
- // start component or start element tokens -- due to the structure of
- // XML and SAX, they just can't show up elsewhere.
-
- directlyInsideSubcomponent = true;
-
- break;
-
- case ATTRIBUTE:
-
- AttributeToken attribute = (AttributeToken) token;
-
- if (directlyInsideSubcomponent)
- {
- addBindingToComponent(loadingElement, activeComponent, attribute);
- }
- else
- add(loadingElement, activeComponent, _pageElementFactory
- .newAttributeElement(attribute));
- break;
-
- default:
- throw new IllegalStateException("Just haven't written that stuff yet.");
- }
-
- }
- }
-
- private void addMixinsToComponent(ComponentPageElement component, EmbeddedComponentModel model,
- String mixins)
- {
- if (model != null)
- {
- for (String mixinClassName : model.getMixinClassNames())
- _pageElementFactory.addMixinByClassName(component, mixinClassName);
- }
-
- if (mixins != null)
- {
- for (String type : mixins.split(","))
- _pageElementFactory.addMixinByTypeName(component, type);
- }
- }
-
- private void addParametersFromModel(EmbeddedComponentModel model,
- ComponentPageElement loadingComponent, ComponentPageElement component)
- {
- for (String name : model.getParameterNames())
- {
- String value = model.getParameterValue(name);
-
- // At some point we may add meta data to control what the default prefix is within a
- // component.
-
- Binding binding = _bindingSource.newBinding(
- "parameter " + name,
- loadingComponent.getComponentResources(),
- component.getComponentResources(),
- InternalConstants.PROP_BINDING_PREFIX,
- value,
- null);
-
- component.addParameter(name, binding);
- }
- }
-
- private String generateEmbeddedId(String embeddedType, IdAllocator idAllocator)
- {
- // TODO: really should trim to last / OR last .
-
- int x = embeddedType.lastIndexOf("/");
-
- // The idAllocator is pre-loaded with all the component ids from the template, so even
- // if the lower-case type matches the id of an existing component, there won't be a name
- // collision.
-
- return idAllocator.allocateId(embeddedType.substring(x + 1).toLowerCase());
- }
-
- // This is for bindings from the template.
- private void addBindingToComponent(ComponentPageElement loadingElement,
- ComponentPageElement component, AttributeToken token)
- {
- String name = token.getName();
- ComponentResources resources = component.getComponentResources();
-
- // If already bound (i.e., from the component class, via @Component), then
- // ignore the value in the template. This may need improving to just ignore
- // the value if it is an unprefixed literal string.
-
- if (resources.isBound(name))
- return;
-
- Binding binding = _bindingSource.newBinding(
- "parameter " + name,
- loadingElement.getComponentResources(),
- component.getComponentResources(),
- InternalConstants.PROP_BINDING_PREFIX,
- token.getValue(),
- token.getLocation());
-
- component.addParameter(name, binding);
- }
-
- private void addRenderBodyElement(ComponentPageElement loadingComponent)
- {
- loadingComponent.addToTemplate(_pageElementFactory.newRenderBodyElement(loadingComponent));
- }
-
- private void add(ComponentPageElement loadingComponent, ComponentPageElement activeComponent,
- PageElement child)
- {
- if (loadingComponent == activeComponent)
- activeComponent.addToTemplate(child);
- else
- activeComponent.addToBody(child);
- }
-
- /** Peeks at the top element on the stack without changing the stack. */
- static <T> T peek(List<T> stack)
- {
- int size = stack.size();
-
- if (size == 0)
- throw new IllegalStateException("Stack is empty.");
-
- return stack.get(size - 1);
- }
-
- /** Pops the top element off the stack and returns it. */
- static <T> T pop(List<T> stack)
- {
- int size = stack.size();
-
- if (size == 0)
- throw new IllegalStateException("Stack is empty.");
-
- return stack.remove(size - 1);
- }
-
- /** Pushes a new element onto the top of the stack. */
- static <T> void push(List<T> stack, T element)
- {
- stack.add(element);
+ return processor.loadPage(pageClassName, locale);
}
/**
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,543 @@
+// Copyright 2007 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.services;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newLinkedList;
+import static org.apache.tapestry.ioc.internal.util.InternalUtils.isBlank;
+import static org.apache.tapestry.ioc.internal.util.InternalUtils.isNonBlank;
+
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.Binding;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.internal.InternalConstants;
+import org.apache.tapestry.internal.parser.AttributeToken;
+import org.apache.tapestry.internal.parser.BodyToken;
+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.ExpansionToken;
+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.internal.structure.BodyPageElement;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.internal.structure.PageElement;
+import org.apache.tapestry.internal.structure.PageImpl;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry.ioc.internal.util.IdAllocator;
+import org.apache.tapestry.ioc.internal.util.OneShotLock;
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.model.EmbeddedComponentModel;
+import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.services.PersistentFieldManager;
+
+/**
+ * Contains all the work-state related to the {@link PageLoaderImpl}.
+ */
+class PageLoaderProcessor
+{
+ private static Runnable NO_OP = new Runnable()
+ {
+ public void run()
+ {
+ // Do nothing.
+ }
+ };
+
+ private LinkedList<ComponentPageElement> _activeElementStack = newLinkedList();
+
+ private boolean _addAttributesAsComponentBindings = false;
+
+ private final BindingSource _bindingSource;
+
+ private final LinkedList<BodyPageElement> _bodyPageElementStack = newLinkedList();
+
+ private final LinkedList<ComponentPageElement> _componentQueue = newLinkedList();
+
+ private final LinkedList<Boolean> _discardEndTagStack = newLinkedList();
+
+ private final LinkedList<Runnable> _endElementStack = newLinkedList();
+
+ private final IdAllocator _idAllocator = new IdAllocator();
+
+ private final LinkFactory _linkFactory;
+
+ private ComponentModel _loadingComponentModel;
+
+ private ComponentPageElement _loadingElement;
+
+ private Locale _locale;
+
+ private final OneShotLock _lock = new OneShotLock();
+
+ private Page _page;
+
+ private final PageElementFactory _pageElementFactory;
+
+ private final PersistentFieldManager _persistentFieldManager;
+
+ private final ComponentTemplateSource _templateSource;
+
+ public PageLoaderProcessor(ComponentTemplateSource templateSource,
+ PageElementFactory pageElementFactory, BindingSource bindingSource,
+ LinkFactory linkFactory, PersistentFieldManager persistentFieldManager)
+ {
+ _templateSource = templateSource;
+ _pageElementFactory = pageElementFactory;
+ _bindingSource = bindingSource;
+ _linkFactory = linkFactory;
+ _persistentFieldManager = persistentFieldManager;
+ }
+
+ // This is for bindings from the template.
+ private void addBindingToComponent(ComponentPageElement component, AttributeToken token)
+ {
+ String name = token.getName();
+ ComponentResources resources = component.getComponentResources();
+
+ // If already bound (i.e., from the component class, via @Component), then
+ // ignore the value in the template. This may need improving to just ignore
+ // the value if it is an unprefixed literal string.
+
+ if (resources.isBound(name))
+ return;
+
+ Binding binding = _bindingSource.newBinding(
+ "parameter " + name,
+ _loadingElement.getComponentResources(),
+ component.getComponentResources(),
+ InternalConstants.PROP_BINDING_PREFIX,
+ token.getValue(),
+ token.getLocation());
+
+ component.addParameter(name, binding);
+ }
+
+ // As element, components, parameters or blocks are started, they push an element onto this
+ // stack. Whenever an end element token is reached, the top value is popped off and executed,
+ // to return state to where it should be.
+
+ private void addMixinsToComponent(ComponentPageElement component, EmbeddedComponentModel model,
+ String mixins)
+ {
+ if (model != null)
+ {
+ for (String mixinClassName : model.getMixinClassNames())
+ _pageElementFactory.addMixinByClassName(component, mixinClassName);
+ }
+
+ if (mixins != null)
+ {
+ for (String type : mixins.split(","))
+ _pageElementFactory.addMixinByTypeName(component, type);
+ }
+ }
+
+ private void addParametersFromModel(EmbeddedComponentModel model,
+ ComponentPageElement loadingComponent, ComponentPageElement component)
+ {
+ for (String name : model.getParameterNames())
+ {
+ String value = model.getParameterValue(name);
+
+ // At some point we may add meta data to control what the default prefix is within a
+ // component.
+
+ Binding binding = _bindingSource.newBinding(
+ "parameter " + name,
+ loadingComponent.getComponentResources(),
+ component.getComponentResources(),
+ InternalConstants.PROP_BINDING_PREFIX,
+ value,
+ null);
+
+ component.addParameter(name, binding);
+ }
+ }
+
+ private void addRenderBodyElement()
+ {
+ PageElement element = _pageElementFactory.newRenderBodyElement(_loadingElement);
+
+ _loadingElement.addToTemplate(element);
+ }
+
+ private void addToBody(PageElement element)
+ {
+ _bodyPageElementStack.peek().addToBody(element);
+ }
+
+ private void attribute(AttributeToken token)
+ {
+ // This kind of bookkeeping is ugly, we probably should have distinct (if very similar)
+ // tokens for attributes and for parameter bindings.
+
+ if (_addAttributesAsComponentBindings)
+ {
+ ComponentPageElement activeElement = _activeElementStack.peek();
+
+ addBindingToComponent(activeElement, token);
+ return;
+ }
+
+ PageElement element = _pageElementFactory.newAttributeElement(token);
+
+ addToBody(element);
+ }
+
+ private void body(BodyToken token)
+ {
+ addRenderBodyElement();
+
+ // BODY tokens are *not* matched by END_ELEMENT tokens. Nor will there be
+ // text or comment content "inside" the BODY.
+ }
+
+ private void comment(CommentToken token)
+ {
+ PageElement commentElement = _pageElementFactory.newCommentElement(token);
+
+ addToBody(commentElement);
+ }
+
+ private void endElement(EndElementToken token)
+ {
+ // discard will be false if the matching start token was for a static element, and will be
+ // true otherwise (component, block, parameter).
+
+ boolean discard = _discardEndTagStack.removeFirst();
+
+ if (!discard)
+ {
+ PageElement element = _pageElementFactory.newEndElement();
+
+ addToBody(element);
+ }
+
+ Runnable runnable = _endElementStack.removeFirst();
+
+ // Used to return environment to prior state.
+
+ runnable.run();
+ }
+
+ private void expansion(ExpansionToken token)
+ {
+ PageElement element = _pageElementFactory.newExpansionElement(_loadingElement
+ .getComponentResources(), token);
+
+ addToBody(element);
+ }
+
+ private String generateEmbeddedId(String embeddedType, IdAllocator idAllocator)
+ {
+ // Component types may be in folders; strip off the folder part for starters.
+
+ int slashx = embeddedType.lastIndexOf("/");
+
+ String baseId = embeddedType.substring(slashx + 1).toLowerCase();
+
+ // The idAllocator is pre-loaded with all the component ids from the template, so even
+ // if the lower-case type matches the id of an existing component, there won't be a name
+ // collision.
+
+ return idAllocator.allocateId(baseId);
+ }
+
+ /**
+ * As currently implemented, this should be invoked just once and then the instance should be
+ * discarded.
+ */
+ public Page loadPage(String pageClassName, Locale locale)
+ {
+ // Ensure that loadPage() may only be invoked once.
+
+ _lock.lock();
+
+ _locale = locale;
+
+ _page = new PageImpl(pageClassName, _locale, _linkFactory, _persistentFieldManager);
+
+ loadRootComponent(pageClassName);
+
+ workComponentQueue();
+
+ // The page is *loaded* before it is attached to the request.
+ // This is to help ensure that no client-specific information leaks
+ // into the page.
+
+ _page.loaded();
+
+ return _page;
+ }
+
+ private void loadRootComponent(String className)
+ {
+ ComponentPageElement rootComponent = _pageElementFactory.newRootComponentElement(
+ _page,
+ className);
+
+ _page.setRootElement(rootComponent);
+
+ _componentQueue.addFirst(rootComponent);
+ }
+
+ /**
+ * Do you smell something? I'm smelling that this class needs to be redesigned to not need a
+ * central method this large and hard to test. I think a lot of instance and local variables
+ * need to be bundled up into some kind of process object. This code is effectively too big to
+ * be tested except through integration testing.
+ */
+ private void loadTemplateForComponent(ComponentPageElement loadingElement)
+ {
+ _loadingElement = loadingElement;
+ _loadingComponentModel = loadingElement.getComponentResources().getComponentModel();
+
+ String componentClassName = _loadingComponentModel.getComponentClassName();
+ ComponentTemplate template = _templateSource.getTemplate(_loadingComponentModel, _locale);
+
+ // When the template for a component is missing, we pretend it consists of just a RenderBody
+ // phase. Missing is not an error ... many component simply do not have a template.
+
+ if (template.isMissing())
+ {
+ addRenderBodyElement();
+ return;
+ }
+
+ // Pre-allocate ids to avoid later name collisions.
+
+ Log log = _loadingComponentModel.getLog();
+
+ Set<String> embeddedIds = CollectionFactory.newSet(_loadingComponentModel
+ .getEmbeddedComponentIds());
+
+ _idAllocator.clear();
+
+ for (String id : template.getComponentIds())
+ {
+ _idAllocator.allocateId(id);
+ embeddedIds.remove(id);
+ }
+
+ if (!embeddedIds.isEmpty())
+ log.error(ServicesMessages.embeddedComponentsNotInTemplate(
+ embeddedIds,
+ componentClassName));
+
+ _addAttributesAsComponentBindings = false;
+
+ // The outermost elements of the template belong in the loading component's template list,
+ // not its body list. This shunt allows everyone else to not have to make that decision,
+ // they can add to the "body" and (if there isn't an active component), the shunt will
+ // add the element to the component's template.
+
+ BodyPageElement shunt = new BodyPageElement()
+ {
+ public void addToBody(PageElement element)
+ {
+ _loadingElement.addToTemplate(element);
+ }
+ };
+
+ _bodyPageElementStack.addFirst(shunt);
+
+ for (TemplateToken token : template.getTokens())
+ {
+ switch (token.getTokenType())
+ {
+ case TEXT:
+ text((TextToken) token);
+ break;
+
+ case EXPANSION:
+ expansion((ExpansionToken) token);
+ break;
+
+ case BODY:
+ body((BodyToken) token);
+ break;
+
+ case START_ELEMENT:
+ startElement((StartElementToken) token);
+ break;
+
+ case START_COMPONENT:
+ startComponent((StartComponentToken) token);
+ break;
+
+ case ATTRIBUTE:
+ attribute((AttributeToken) token);
+ break;
+
+ case END_ELEMENT:
+ endElement((EndElementToken) token);
+ break;
+
+ case COMMENT:
+ comment((CommentToken) token);
+ break;
+
+ default:
+ throw new IllegalStateException("Not implemented yet: " + token);
+ }
+ }
+
+ // For neatness / symmetry:
+
+ _bodyPageElementStack.removeFirst(); // the shunt
+
+ // TODO: Check that all stacks are empty. That should never happen, as long
+ // as the ComponentTemplate is valid.
+ }
+
+ private void startComponent(StartComponentToken token)
+ {
+ String elementName = token.getElementName();
+
+ // Initial guess: the type from the token (but this may be null in many cases).
+ String embeddedType = token.getType();
+
+ String embeddedId = token.getId();
+
+ String embeddedComponentClassName = null;
+
+ // We know that if embeddedId is null, embeddedType is not.
+
+ if (embeddedId == null)
+ embeddedId = generateEmbeddedId(embeddedType, _idAllocator);
+
+ EmbeddedComponentModel embeddedModel = _loadingComponentModel
+ .getEmbeddedComponentModel(embeddedId);
+
+ if (embeddedModel != null)
+ {
+ String modelType = embeddedModel.getComponentType();
+
+ if (isNonBlank(modelType) && embeddedType != null)
+ {
+ Log log = _loadingComponentModel.getLog();
+ log.error(ServicesMessages.compTypeConflict(embeddedId, embeddedType, modelType));
+ }
+
+ embeddedType = modelType;
+ embeddedComponentClassName = embeddedModel.getComponentClassName();
+ }
+
+ if (isBlank(embeddedType) && isBlank(embeddedComponentClassName))
+ {
+ // non-null means its invisible instrumentation; the Any component
+ // will mimic the actual element, w/ body and informal parameters.
+
+ if (elementName != null)
+ embeddedType = "Any";
+ else
+ throw new TapestryException(ServicesMessages.noTypeForEmbeddedComponent(
+ embeddedId,
+ _loadingComponentModel.getComponentClassName()), token, null);
+ }
+
+ ComponentPageElement newComponent = _pageElementFactory.newComponentElement(
+ _page,
+ _loadingElement,
+ embeddedId,
+ embeddedType,
+ embeddedComponentClassName,
+ elementName,
+ token.getLocation());
+
+ addMixinsToComponent(newComponent, embeddedModel, token.getMixins());
+
+ if (embeddedModel != null)
+ addParametersFromModel(embeddedModel, _loadingElement, newComponent);
+
+ addToBody(newComponent);
+
+ _componentQueue.addFirst(newComponent);
+
+ // Any attribute tokens that immediately follow should be
+ // used to bind parameters.
+
+ _addAttributesAsComponentBindings = true;
+
+ // Any attributes or parameters that come up belong on this component.
+
+ _activeElementStack.addFirst(newComponent);
+
+ // Set things up so that content inside the component is added to the component's body.
+
+ _bodyPageElementStack.addFirst(newComponent);
+
+ // The start tag is not added to the body of the component, so neither should
+ // the end tag.
+
+ _discardEndTagStack.addFirst(true);
+
+ // And clean that up when the end element is reached.
+
+ Runnable cleanup = new Runnable()
+ {
+ public void run()
+ {
+ _activeElementStack.removeFirst();
+ _bodyPageElementStack.removeFirst();
+ }
+ };
+
+ _endElementStack.addFirst(cleanup);
+ }
+
+ private void startElement(StartElementToken token)
+ {
+ PageElement element = _pageElementFactory.newStartElement(token);
+
+ addToBody(element);
+
+ // Controls how attributes are interpretted.
+ _addAttributesAsComponentBindings = false;
+
+ // Start will be matched by end:
+
+ // Do NOT discard the end tag; add it to the body.
+
+ _discardEndTagStack.addFirst(false);
+ _endElementStack.addFirst(NO_OP);
+ }
+
+ private void text(TextToken token)
+ {
+ PageElement element = _pageElementFactory.newTextElement(token);
+
+ addToBody(element);
+ }
+
+ /** Works the component queue, until exausted. */
+ private void workComponentQueue()
+ {
+ while (!_componentQueue.isEmpty())
+ {
+ ComponentPageElement componentElement = _componentQueue.removeFirst();
+
+ loadTemplateForComponent(componentElement);
+ }
+ }
+}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -325,4 +325,24 @@
.getCompleteId(), result, methodDescription, ClassFabUtils.getJavaClassName(result
.getClass()), InternalUtils.joinSorted(classNames));
}
+
+ static String undefinedTapestryElement(String elementName)
+ {
+ return MESSAGES.format("undefined-tapestry-element", elementName);
+ }
+
+ static String undefinedTapestryAttribute(String elementName, String attributeName,
+ String allowedAttributeName)
+ {
+ return MESSAGES.format(
+ "undefined-tapestry-attribute",
+ elementName,
+ attributeName,
+ allowedAttributeName);
+ }
+
+ static String parameterElementNameRequired()
+ {
+ return MESSAGES.get("parameter-element-name-required");
+ }
}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -25,6 +25,7 @@
import org.apache.commons.logging.Log;
import org.apache.tapestry.internal.parser.AttributeToken;
+import org.apache.tapestry.internal.parser.BlockToken;
import org.apache.tapestry.internal.parser.BodyToken;
import org.apache.tapestry.internal.parser.CDATAToken;
import org.apache.tapestry.internal.parser.CommentToken;
@@ -32,6 +33,7 @@
import org.apache.tapestry.internal.parser.ComponentTemplateImpl;
import org.apache.tapestry.internal.parser.EndElementToken;
import org.apache.tapestry.internal.parser.ExpansionToken;
+import org.apache.tapestry.internal.parser.ParameterToken;
import org.apache.tapestry.internal.parser.StartComponentToken;
import org.apache.tapestry.internal.parser.StartElementToken;
import org.apache.tapestry.internal.parser.TemplateToken;
@@ -277,11 +279,10 @@
if (TAPESTRY_SCHEMA_5_0_0.equals(uri))
{
- startTapestryElement(localName, attributes);
+ startTapestryElement(qName, localName, attributes);
return;
}
- // TODO: Handle tapestry namespace attributes in ordinary namespace elements?
// TODO: Handle interpolations inside attributes?
startStaticElement(localName, attributes);
@@ -387,11 +388,11 @@
return _insideBody;
}
- private void startTapestryElement(String localName, Attributes attributes)
+ private void startTapestryElement(String qname, String localName, Attributes attributes)
{
if (localName.equals("comp"))
{
- startComponent(localName, attributes);
+ startComponent(attributes);
return;
}
@@ -401,10 +402,69 @@
return;
}
- throw new IllegalArgumentException("Unknown localName: '" + localName + "'.");
+ if (localName.equals("parameter"))
+ {
+ startParameter(attributes);
+ return;
+ }
+
+ if (localName.equals("block"))
+ {
+ startBlock(attributes);
+ return;
+ }
+
+ throw new TapestryException(ServicesMessages.undefinedTapestryElement(qname),
+ getCurrentLocation(), null);
}
- private void startComponent(String localName, Attributes attributes)
+ private void startBlock(Attributes attributes)
+ {
+ String blockId = findSingleParameter("block", "id", attributes);
+
+ // null is ok for blockId
+
+ _tokens.add(new BlockToken(blockId, getCurrentLocation()));
+ }
+
+ private void startParameter(Attributes attributes)
+ {
+ String parameterName = findSingleParameter("parameter", "name", attributes);
+
+ if (InternalUtils.isBlank(parameterName))
+ throw new TapestryException(ServicesMessages.parameterElementNameRequired(),
+ getCurrentLocation(), null);
+
+ _tokens.add(new ParameterToken(parameterName, getCurrentLocation()));
+ }
+
+ private String findSingleParameter(String elementName, String attributeName,
+ Attributes attributes)
+ {
+ String result = null;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ String name = attributes.getLocalName(i);
+
+ if (name.equals(attributeName))
+ {
+ result = attributes.getValue(i);
+ continue;
+ }
+
+ // Only the name attribute is allowed.
+
+ throw new TapestryException(ServicesMessages.undefinedTapestryAttribute(
+ elementName,
+ name,
+ attributeName), getCurrentLocation(), null);
+ }
+
+ return result;
+ }
+
+ private void startComponent(Attributes attributes)
{
String id = null;
String type = null;
@@ -501,8 +561,8 @@
processTextBuffer();
- // Remove excess whitespace. The Comment DOM node will add a leadig and trailing space.
-
+ // Remove excess whitespace. The Comment DOM node will add a leadig and trailing space.
+
String comment = new String(ch, start, length).trim();
// TODO: Perhaps comments need to be "aggregated" the same way we aggregate text and CDATA.
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BlockImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BlockImpl.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BlockImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BlockImpl.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,55 @@
+// Copyright 2007 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.structure;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+
+import java.util.List;
+
+import org.apache.tapestry.Block;
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.ioc.BaseLocatable;
+import org.apache.tapestry.ioc.Location;
+import org.apache.tapestry.runtime.RenderCommand;
+import org.apache.tapestry.runtime.RenderQueue;
+
+public class BlockImpl extends BaseLocatable implements Block, BodyPageElement, RenderCommand
+{
+ // We could lazily create this, but for <t:block> and <t:parameter>, the case
+ // for an empty block is extremely rare.
+
+ private final List<PageElement> _elements = newList();
+
+ public BlockImpl(Location location)
+ {
+ super(location);
+ }
+
+ public void addToBody(PageElement element)
+ {
+ _elements.add(element);
+ }
+
+ /**
+ * Pushes all the elements of the body of this block onto the queue in appropriate order.
+ */
+ public void render(MarkupWriter writer, RenderQueue queue)
+ {
+ int count = _elements.size();
+ for (int i = count - 1; i >= 0; i--)
+ queue.push(_elements.get(i));
+ }
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BodyPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BodyPageElement.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BodyPageElement.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/BodyPageElement.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,31 @@
+// Copyright 2007 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.structure;
+
+import org.apache.tapestry.internal.services.PageLoader;
+
+/**
+ * A type of {@link PageElement} that has a body that can be added to. This is part of the
+ * constuction phase that is faciliated by the {@link PageLoader}.
+ */
+public interface BodyPageElement
+{
+ /**
+ * Used during the construction of the page. Adds a page element as part of the body of the
+ * component. The body of a component is defined as the portion of the container's template
+ * directly enclosed by component's start and end elements.
+ */
+ void addToBody(PageElement element);
+}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -12,94 +12,85 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.internal.structure;
-
-import org.apache.tapestry.ComponentResourcesCommon;
-import org.apache.tapestry.internal.InternalComponentResources;
-import org.apache.tapestry.internal.InternalComponentResourcesCommon;
-import org.apache.tapestry.internal.services.Instantiator;
-import org.apache.tapestry.runtime.ComponentEvent;
-import org.apache.tapestry.runtime.Component;
-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
- * are, in fact, components (rather than just static markup).
- */
-public interface ComponentPageElement extends ComponentResourcesCommon,
- InternalComponentResourcesCommon, PageElement, RenderCommand
-{
- /**
- * Returns the core component associated with this page element (as opposed to any mixins
- * attached to the component).
- */
- Component getComponent();
-
- /** Returns the resources associated with the core component. */
- InternalComponentResources getComponentResources();
-
- /** Returns the page which contains this component. */
- Page getContainingPage();
-
- /**
- * Containing component (or null for the root component of a page).
- */
- ComponentPageElement getContainerElement();
-
- /**
- * Used during the construction of a page. Adds a page element as part of the template for this
- * page element. A page element will eventually render by sequentially rendering these elements.
- * A page elements template is really just the outermost portions of the component's template
- * ... where a template contains elements that are reall components, those components will
- * receive portions of the template as their body.
- */
-
- void addToTemplate(PageElement element);
-
- /**
- * Used during the construction of the page. Adds a page element as part of the body of the
- * component. The body of a component is defined as the portion of the container's template
- * directly enclosed by component's start and end elements.
- */
- void addToBody(PageElement element);
-
- /**
- * Adds a component to its container. The embedded component's id must be unique within the
- * container.
- */
- void addEmbeddedElement(ComponentPageElement child);
-
- /**
- * Adds a mixin.
- *
- * @param instantiator
- * used to instantiate an instance of the mixin
- */
- void addMixin(Instantiator instantiator);
-
- /**
- * Retrieves a component page element by its id.
- *
- * @param id
- * used to locate the element
- * @return the page element
- * @throws IllegalArgumentException
- * if no component exists with the given id
- */
- ComponentPageElement getEmbeddedElement(String id);
-
- /** Invoked when the component should render its body. */
- void enqueueBeforeRenderBody(RenderQueue queue);
-
- /**
- * Asks each mixin and component to
- * {@link Component#handleComponentEvent(ComponentEvent)}, returning true if any
- * handler was found.
- *
- * @param event
- * to be handled
- * @return true if a handler was found
- */
- boolean handleEvent(ComponentEvent event);
-}
+package org.apache.tapestry.internal.structure;
+
+import org.apache.tapestry.ComponentResourcesCommon;
+import org.apache.tapestry.internal.InternalComponentResources;
+import org.apache.tapestry.internal.InternalComponentResourcesCommon;
+import org.apache.tapestry.internal.services.Instantiator;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.ComponentEvent;
+import org.apache.tapestry.runtime.RenderQueue;
+
+/**
+ * Extended version of {@link org.apache.tapestry.internal.structure.PageElement} for elements that
+ * are, in fact, components (rather than just static markup).
+ */
+public interface ComponentPageElement extends ComponentResourcesCommon,
+ InternalComponentResourcesCommon, PageElement, BodyPageElement
+{
+ /**
+ * Returns the core component associated with this page element (as opposed to any mixins
+ * attached to the component).
+ */
+ Component getComponent();
+
+ /** Returns the resources associated with the core component. */
+ InternalComponentResources getComponentResources();
+
+ /** Returns the page which contains this component. */
+ Page getContainingPage();
+
+ /**
+ * Containing component (or null for the root component of a page).
+ */
+ ComponentPageElement getContainerElement();
+
+ /**
+ * Used during the construction of a page. Adds a page element as part of the template for this
+ * page element. A page element will eventually render by sequentially rendering these elements.
+ * A page elements template is really just the outermost portions of the component's template
+ * ... where a template contains elements that are all components, those components will receive
+ * portions of the template as their body.
+ */
+
+ void addToTemplate(PageElement element);
+
+ /**
+ * Adds a component to its container. The embedded component's id must be unique within the
+ * container.
+ */
+ void addEmbeddedElement(ComponentPageElement child);
+
+ /**
+ * Adds a mixin.
+ *
+ * @param instantiator
+ * used to instantiate an instance of the mixin
+ */
+ void addMixin(Instantiator instantiator);
+
+ /**
+ * Retrieves a component page element by its id.
+ *
+ * @param id
+ * used to locate the element
+ * @return the page element
+ * @throws IllegalArgumentException
+ * if no component exists with the given id
+ */
+ ComponentPageElement getEmbeddedElement(String id);
+
+ /** Invoked when the component should render its body. */
+ void enqueueBeforeRenderBody(RenderQueue queue);
+
+ /**
+ * Asks each mixin and component to {@link Component#handleComponentEvent(ComponentEvent)},
+ * returning true if any handler was found.
+ *
+ * @param event
+ * to be handled
+ * @return true if a handler was found
+ */
+ boolean handleEvent(ComponentEvent event);
+}
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -69,4 +69,7 @@
validator-specification-parse-error=Unexpected character '%s' at position %d of input string: %s
unknown-translator-type=Unknown translator type '%s'. Configured translators are %s.
missing-from-environment=No object of type %s is available from the Environment. Available types are %s.
-invalid-component-event-result=An event handler for component %s returned the value %s (from method %s). Return type %s can not be handled. Configured return types are %s.
\ No newline at end of file
+invalid-component-event-result=An event handler for component %s returned the value %s (from method %s). Return type %s can not be handled. Configured return types are %s.
+undefined-tapestry-element=Element <%s> is in the Tapestry namespace, but is not a recognized Tapestry template element.
+undefined-tapestry-attribute=Element <%s> does not support an attribute named '%s'. The only allowed attribute name is '%s'.
+parameter-element-name-required=The name attribute of the <parameter> element must be specified.
\ 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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -2,14 +2,89 @@
<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"/>
- <xs:attribute name="type" type="xs:string"/>
- <xs:anyAttribute/>
- </xs:complexType>
+ <xs:annotation>
+ <xs:documentation> A component within the template. Allows any attributes, these are
+ bound to parameters of the component. A component will have either an id, a type, or
+ both (they are both optional, with the requirement for one or the other implemented
+ internally). </xs:documentation>
+ </xs:annotation>
+ <xs:complexType mixed="true">
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation> A unique identifier for the component within the template. If the type
+ is omitted, Tapestry will provide a unique id for the component.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The name of a component type. This is optional, as the type may be determined elsewhere
+ or otherwise defaulted by Tapestry.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="mixins" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ A comma-seperated list of mixin type names. Mixins are optional and provide additional
+ functionality to the base component.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute>
+ <xs:annotation>
+ <xs:documentation>
+ Any additional attributes are used to bind parameters of the component (or of mixins of the component).
+ </xs:documentation>
+ </xs:annotation>
+ </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:element name="body">
+ <xs:annotation>
+ <xs:documentation>
+ Defines the position within the template that the body of the component (the portion of the container's template
+ enclosed by the component) will be rendered. This is optional, and only applies to components that wish to render
+ their body within their template.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="parameter">
+ <xs:annotation>
+ <xs:documentation>
+ A structured parameter passed to a component as a single object of type Block. The receiving component
+ can get the Block to render. A parameter should always be enclosed by a component element
+ (either an explicit comp element, or an ordinary element instrumented with a Tapestry type or id).
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The name of the parameter to be bound to the Block.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="block">
+ <xs:annotation>
+ <xs:documentation>
+ A block is simply a container of other elements. Blocks do not render themselves or their bodies normally, unless
+ specifically directed to.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation>
+ An optional identifier that is used to reference the block from inside the Java class.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
</xs:schema>
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?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- 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 Thu Jan 11 17:57:38 2007
@@ -14,6 +14,7 @@
package org.apache.tapestry.internal.services;
+import static java.lang.String.format;
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newSet;
import static org.easymock.EasyMock.contains;
@@ -23,22 +24,26 @@
import org.apache.commons.logging.Log;
import org.apache.tapestry.internal.parser.AttributeToken;
+import org.apache.tapestry.internal.parser.BlockToken;
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.ExpansionToken;
+import org.apache.tapestry.internal.parser.ParameterToken;
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.internal.parser.TokenType;
import org.apache.tapestry.ioc.Locatable;
import org.apache.tapestry.ioc.Location;
import org.apache.tapestry.ioc.Resource;
import org.apache.tapestry.ioc.internal.util.ClasspathResource;
import org.apache.tapestry.ioc.internal.util.TapestryException;
import org.apache.tapestry.test.TapestryTestCase;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
@@ -328,24 +333,6 @@
}
@Test
- public void mixin_requires_id_or_type()
- {
- try
- {
- tokens("mixin_requires_id_or_type.html");
- unreachable();
- }
- catch (TapestryException ex)
- {
- assertTrue(ex
- .getMessage()
- .contains(
- "You may not specify mixins for element <span> because it does not represent a component (which requires either an id attribute or a type attribute)."));
- assertNotNull(ex.getLocation());
- }
- }
-
- @Test
void body_element()
{
List<TemplateToken> tokens = tokens("body_element.html");
@@ -359,22 +346,6 @@
}
@Test
- void illegal_nesting_within_body_element()
- {
- 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()
{
Log log = newLog();
@@ -458,21 +429,6 @@
}
@Test
- public void component_without_id_or_type()
- {
- try
- {
- tokens("component_without_id_or_type.html");
- unreachable();
- }
- catch (TapestryException ex)
- {
- assertEquals(ex.getCause().getMessage(), ServicesMessages.compRequiresIdOrType());
- assertEquals(ex.getLocation().getLine(), 2);
- }
- }
-
- @Test
public void component_ids()
{
Log log = newLog();
@@ -574,5 +530,91 @@
assertEquals(t1.getName(), "exp");
assertEquals(t1.getValue(), "${not-an-expansion}");
+ }
+
+ @Test
+ public void parameter_element()
+ {
+ List<TemplateToken> tokens = tokens("parameter_element.html");
+
+ ParameterToken token4 = get(tokens, 4);
+ assertEquals(token4.getName(), "fred");
+
+ CommentToken token6 = get(tokens, 6);
+ assertEquals(token6.getComment(), "fred content");
+
+ TemplateToken token8 = get(tokens, 8);
+
+ assertEquals(token8.getTokenType(), TokenType.END_ELEMENT);
+ }
+
+ @Test
+ public void block_element()
+ {
+ List<TemplateToken> tokens = tokens("block_element.html");
+
+ BlockToken token2 = get(tokens, 2);
+ assertEquals(token2.getId(), "block0");
+
+ CommentToken token4 = get(tokens, 4);
+ assertEquals(token4.getComment(), "block0 content");
+
+ BlockToken token8 = get(tokens, 8);
+ assertNull(token8.getId());
+
+ CommentToken token10 = get(tokens, 10);
+ assertEquals(token10.getComment(), "anon block content");
+ }
+
+ @DataProvider(name = "parse_failure_data")
+ public Object[][] parse_failure_data()
+ {
+ return new Object[][]
+ {
+ {
+ "mixin_requires_id_or_type.html",
+ "You may not specify mixins for element <span> because it does not represent a component (which requires either an id attribute or a type attribute).",
+ 2 },
+ { "illegal_nesting_within_body_element.html",
+ "Element 'xyz' is nested within a Tapestry body element", 2 },
+ { "component_without_id_or_type.html", ServicesMessages.compRequiresIdOrType(), 2 },
+ {
+ "unexpected_element_in_tapestry_namespace.html",
+ "Element <t:fubar> is in the Tapestry namespace, but is not a recognized Tapestry template element.",
+ 3 },
+ {
+ "unexpected_attribute_in_parameter_element.html",
+ "Element <parameter> does not support an attribute named 'grok'. The only allowed attribute name is 'name'.",
+ 4 },
+ { "name_attribute_of_parameter_element_omitted.html",
+ "The name attribute of the <parameter> element must be specified.", 4 },
+ { "name_attribute_of_parameter_element_blank.html",
+ "The name attribute of the <parameter> element must be specified.", 4 },
+ {
+ "unexpected_attribute_in_block_element.html",
+ "Element <block> does not support an attribute named 'name'. The only allowed attribute name is 'id'.",
+ 3 },
+
+ };
+ }
+
+ @Test(dataProvider = "parse_failure_data")
+ public void parse_failure(String fileName, String errorMessageSubstring, int expectedLine)
+ {
+ try
+ {
+ parse(fileName);
+ unreachable();
+ }
+ catch (TapestryException ex)
+ {
+ if (!ex.getMessage().contains(errorMessageSubstring))
+ {
+ throw new AssertionError(format("Message [%s] does not contain substring [%s].", ex
+ .getMessage(), errorMessageSubstring));
+ }
+
+ assertEquals(ex.getLocation().getLine(), expectedLine);
+ }
}
}
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/BlockImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/BlockImplTest.java?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/BlockImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/BlockImplTest.java Thu Jan 11 17:57:38 2007
@@ -0,0 +1,61 @@
+// Copyright 2007 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.structure;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.runtime.RenderQueue;
+import org.testng.annotations.Test;
+
+public class BlockImplTest extends InternalBaseTestCase
+{
+ @Test
+ public void empty_block()
+ {
+ BlockImpl block = new BlockImpl(null);
+ RenderQueue queue = newRenderQueue();
+ MarkupWriter writer = newMarkupWriter();
+
+ replay();
+
+ block.render(writer, queue);
+
+ verify();
+ }
+
+ @Test
+ public void body_pushed_to_queue_backwards()
+ {
+ BlockImpl block = new BlockImpl(null);
+ RenderQueue queue = newRenderQueue();
+ MarkupWriter writer = newMarkupWriter();
+ PageElement element1 = newPageElement();
+ PageElement element2 = newPageElement();
+
+ getMocksControl().checkOrder(true);
+
+ queue.push(element2);
+ queue.push(element1);
+
+ replay();
+
+ block.addToBody(element1);
+ block.addToBody(element2);
+
+ block.render(writer, queue);
+
+ verify();
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterConflict.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterConflict.html?view=diff&rev=495465&r1=495464&r2=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterConflict.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterConflict.html Thu Jan 11 17:57:38 2007
@@ -1,4 +1,3 @@
-
<t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<p> This component demonstrates that template values are overriden by
bindings inside the @Component annotation, in the component class.</p>
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/block_element.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/block_element.html?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/block_element.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/block_element.html Thu Jan 11 17:57:38 2007
@@ -0,0 +1,10 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+ <t:block id="block0">
+ <!-- block0 content -->
+ </t:block>
+
+ <t:block>
+ <!-- anon block content -->
+ </t:block>
+</html>
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_blank.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_blank.html?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_blank.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_blank.html Thu Jan 11 17:57:38 2007
@@ -0,0 +1,7 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+ <t:comp id="foo">
+ <t:parameter name=""/>
+ </t:comp>
+
+</html>
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_omitted.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_omitted.html?view=auto&rev=495465
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_omitted.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/internal/services/name_attribute_of_parameter_element_omitted.html Thu Jan 11 17:57:38 2007
@@ -0,0 +1,7 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+ <t:comp id="foo">
+ <t:parameter/>
+ </t:comp>
+
+</html>