You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/09/14 19:51:15 UTC

svn commit: r443428 [1/2] - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/annotations/ main/java/org/apache/tapestry/internal/bindings/ main/java/org/apache/tapestry/internal/ioc/ main/java...

Author: hlship
Date: Thu Sep 14 10:51:13 2006
New Revision: 443428

URL: http://svn.apache.org/viewvc?view=rev&rev=443428
Log:
Add support for the @Component annotation, a field annotation used to define embedded components.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Component.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableEmbeddedComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Countdown.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ParameterConflict.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/Countdown.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterConflict.html
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/EndElementToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoader.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/RetainWorker.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/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
    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/structure/StructureStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/component-classes.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/templates.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Loop.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/components/Border.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/MerryChristmas.html

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java Thu Sep 14 10:51:13 2006
@@ -50,6 +50,15 @@
     ComponentLifecycle getComponent();
 
     /**
+     * Returns an embedded component, given the component's id.
+     * 
+     * @throws IllegalArgumentException
+     *             if this component does not contain a component with the given id
+     */
+
+    ComponentLifecycle getEmbeddedComponent(String embeddedId);
+
+    /**
      * Returns true if the component is currently rendering, false otherwise. This is most often
      * used to determine if parameter values should be cached.
      */

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Component.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Component.java?view=auto&rev=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Component.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Component.java Thu Sep 14 10:51:13 2006
@@ -0,0 +1,45 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Used to define an <em>embedded component</em> within another component.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Target(FIELD)
+@Documented
+@Retention(RUNTIME)
+public @interface Component {
+
+    /**
+     * The id of the component. When left blank (the default), the component id is determined from
+     * the field name.
+     */
+    String id() default "";
+
+    /**
+     * Parameter bindings for the component. Each value in the array is of the form "name=value".
+     * The value is a binding expression, with a default binding prefix of "prop:".
+     */
+    String[] parameters() default {};
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java Thu Sep 14 10:51:13 2006
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry.Location;
 import org.apache.tapestry.internal.TapestryException;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 
 /**
  * Base class for bindings created by the
@@ -24,6 +25,7 @@
  * 
  * @author Howard M. Lewis Ship
  */
+@SuppressNullCheck
 public abstract class BasePropBinding extends AbstractBinding
 {
     private final String _toString;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java Thu Sep 14 10:51:13 2006
@@ -15,12 +15,14 @@
 package org.apache.tapestry.internal.bindings;
 
 import org.apache.tapestry.Location;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 
 /**
  * Binding type for literal string values, usually supplied in-line in the component template.
  * 
  * @author Howard M. Lewis Ship
  */
+@SuppressNullCheck
 public class LiteralBinding extends AbstractBinding
 {
     private final String _description;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java Thu Sep 14 10:51:13 2006
@@ -17,6 +17,7 @@
 import org.apache.tapestry.Binding;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Location;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.services.BindingFactory;
 
 /**
@@ -25,6 +26,7 @@
  * @author Howard M. Lewis Ship
  * @see org.apache.tapestry.internal.bindings.LiteralBinding
  */
+@SuppressNullCheck
 public class LiteralBindingFactory implements BindingFactory
 {
     public Binding newBinding(String description, ComponentResources component, String expression,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java Thu Sep 14 10:51:13 2006
@@ -25,6 +25,7 @@
 import org.apache.tapestry.events.InvalidationListener;
 import org.apache.tapestry.internal.TapestryException;
 import org.apache.tapestry.internal.annotations.Concurrent;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.ClassFabUtils;
 import org.apache.tapestry.ioc.services.ClassFactory;
@@ -43,6 +44,7 @@
  * @author Howard M. Lewis Ship
  */
 @Concurrent
+@SuppressNullCheck
 public class PropBindingFactory implements BindingFactory, InvalidationListener
 {
     private final PropertyAccess _propertyAccess;
@@ -160,7 +162,7 @@
 
                 // Cast the value to the wrapper type, and then extract the primitive
                 // value from that.
-                
+
                 builder.addln("((%s) $1).%s();", wrapperType, unwrapMethod);
             }
             else

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java Thu Sep 14 10:51:13 2006
@@ -230,4 +230,44 @@
 
         return null;
     }
+
+    /**
+     * Extracts the string keys from a map and returns them in sorted order.
+     * 
+     * @param <V>
+     * @param map
+     *            the map to extract keys from
+     * @return the sorted keys, or the empty set if map is null
+     */
+    @SuppressNullCheck
+    public static <V> List<String> sortedKeys(Map<String, V> map)
+    {
+        if (map == null)
+            return Collections.emptyList();
+
+        List<String> keys = newList(map.keySet());
+
+        Collections.sort(keys);
+
+        return keys;
+    }
+
+    /**
+     * Gets a value from a map (which may be null).
+     * 
+     * @param <K>
+     * @param <V>
+     * @param map
+     *            the map to extract from (may be null)
+     * @param key
+     * @return the value from the map, or null if the map is null
+     */
+    @SuppressNullCheck
+    public static <K, V> V get(Map<K, V> map, K key)
+    {
+        if (map == null)
+            return null;
+
+        return map.get(key);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java Thu Sep 14 10:51:13 2006
@@ -30,4 +30,19 @@
     {
         return MESSAGES.format("duplicate-parameter", parameterName, componentName);
     }
+
+    static String duplicateParameterValue(String parameterName, String componentId,
+            String componentClassName)
+    {
+        return MESSAGES.format(
+                "duplicate-parameter-value",
+                parameterName,
+                componentId,
+                componentClassName);
+    }
+
+    static String duplicateComponentId(String id, String componentClassName)
+    {
+        return MESSAGES.format("duplicate-component-id", id, componentClassName);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java Thu Sep 14 10:51:13 2006
@@ -20,7 +20,10 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.Resource;
+import org.apache.tapestry.internal.ioc.IOCUtilities;
+import org.apache.tapestry.model.EmbeddedComponentModel;
 import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.model.MutableEmbeddedComponentModel;
 import org.apache.tapestry.model.ParameterModel;
 import org.apache.tapestry.util.Defense;
 
@@ -42,6 +45,8 @@
 
     private Map<String, ParameterModel> _parameters;
 
+    private Map<String, EmbeddedComponentModel> _embeddedComponents;
+
     public MutableComponentModelImpl(String componentClassName, Log log, Resource baseResource)
     {
         _componentClassName = componentClassName;
@@ -111,6 +116,39 @@
         Collections.sort(names);
 
         return names;
+    }
+
+    public MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type)
+    {
+        // TODO: Parent compent model? Or would we simply override the parent?
+
+        if (_embeddedComponents == null)
+            _embeddedComponents = newMap();
+        else if (_embeddedComponents.containsKey(id))
+            throw new IllegalArgumentException(ModelMessages.duplicateComponentId(
+                    id,
+                    _componentClassName));
+
+        MutableEmbeddedComponentModel embedded = new MutableEmbeddedComponentModelImpl(id, type,
+                _componentClassName);
+
+        _embeddedComponents.put(id, embedded);
+
+        return embedded; // So that parameters can be filled in
+    }
+
+    public List<String> getEmbeddedComponentIds()
+    {
+        // TODO: Parent compent model?
+
+        return IOCUtilities.sortedKeys(_embeddedComponents);
+    }
+
+    public EmbeddedComponentModel getEmbeddedComponentModel(String componentId)
+    {
+        // TODO: Parent compent model?
+
+        return IOCUtilities.get(_embeddedComponents, componentId);
     }
 
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java?view=auto&rev=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java Thu Sep 14 10:51:13 2006
@@ -0,0 +1,85 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.model;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry.internal.ioc.IOCUtilities;
+import org.apache.tapestry.model.MutableEmbeddedComponentModel;
+
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class MutableEmbeddedComponentModelImpl implements MutableEmbeddedComponentModel
+{
+    private final String _id;
+
+    private final String _componentType;
+
+    private final String _componentClassName;
+
+    private Map<String, String> _parameters;
+
+    public MutableEmbeddedComponentModelImpl(String id, String componentType,
+            String componentClassName)
+    {
+        _id = id;
+        _componentType = componentType;
+        _componentClassName = componentClassName;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("EmbeddedComponentModel[id=%s type=%s]", _id, _componentType);
+    }
+
+    public void addParameter(String name, String value)
+    {
+        if (_parameters == null)
+            _parameters = newMap();
+        else if (_parameters.containsKey(name))
+            throw new IllegalArgumentException(ModelMessages.duplicateParameterValue(
+                    name,
+                    _id,
+                    _componentClassName));
+
+        _parameters.put(name, value);
+    }
+
+    public String getId()
+    {
+        return _id;
+    }
+
+    public String getComponentType()
+    {
+        return _componentType;
+    }
+
+    public List<String> getParameterNames()
+    {
+        return IOCUtilities.sortedKeys(_parameters);
+    }
+
+    public String getParameterValue(String parameterName)
+    {
+        return IOCUtilities.get(_parameters, parameterName);
+    }
+
+}

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=443428&r1=443427&r2=443428
==============================================================================
--- 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 Sep 14 10:51:13 2006
@@ -15,12 +15,14 @@
 package org.apache.tapestry.internal.parser;
 
 import org.apache.tapestry.Location;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 
 /**
  * Ends a previously started element (including components, parameters, etc.).
  * 
  * @author Howard M. Lewis Ship
  */
+@SuppressNullCheck
 public class EndElementToken extends TemplateToken
 {
     public EndElementToken(Location location)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/parser/TemplateToken.java Thu Sep 14 10:51:13 2006
@@ -27,7 +27,7 @@
 {
     private final TokenType _tokenType;
 
-    public TemplateToken(TokenType tokenType, Location location)
+    protected TemplateToken(TokenType tokenType, Location location)
     {
         super(location);
         _tokenType = tokenType;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java Thu Sep 14 10:51:13 2006
@@ -19,12 +19,15 @@
 import org.apache.tapestry.Binding;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Location;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.services.BindingFactory;
 import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.util.Defense;
 
 /**
  * @author Howard M. Lewis Ship
  */
+@SuppressNullCheck
 public class BindingSourceImpl implements BindingSource
 {
     private final Map<String, BindingFactory> _factories;
@@ -37,6 +40,12 @@
     public Binding newBinding(String description, ComponentResources component,
             String defaultPrefix, String expression, Location location)
     {
+        Defense.notBlank(description, "description");
+        Defense.notNull(component, "component");
+        Defense.notBlank(defaultPrefix, "defaultPrefix");
+        Defense.notBlank(expression, "expression");
+        // Location might be null
+
         String subexpression = expression;
         int colonx = expression.indexOf(':');
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java Thu Sep 14 10:51:13 2006
@@ -23,36 +23,38 @@
 {
     private final ComponentInstantiatorSource _componentInstantiatorSource;
 
+    private String _appPagePackage;
+
+    private String _appComponentPackage;
+
     public ComponentClassResolverImpl(ComponentInstantiatorSource componentInstantiatorSource)
     {
         _componentInstantiatorSource = componentInstantiatorSource;
     }
 
-    private String _appPagePackage;
-
-    private String _appComponentPackage;
-
     public String resolvePageNameToClassName(String pageName)
     {
         return resolve(_appPagePackage, pageName);
     }
 
-    private String resolve(String packageName, String inputClassName)
+    public String resolveComponentTypeToClassName(String componentType)
     {
-        if (inputClassName.contains("."))
-            return inputClassName;
+        return resolve(_appComponentPackage, componentType);
+    }
+
+    private String resolve(String packageName, String partialClassName)
+    {
+        if (partialClassName.contains("."))
+            return partialClassName;
 
         // For now, assume it is in the application page/component package.
-        // So much more to do: search libraries, handle
-        // sub-dirs/sub-packages, search in the
-        // framework if not found elsewhere.
 
-        return packageName + "." + inputClassName;
-    }
+        // For the moment, the replace() converts folder names into subpackage names. Again,
+        // sometimes the initial package name(s) will indicate a library, rather than the
+        // application.
+        // That code is coming.
 
-    public String resolveComponentTypeToClassName(String componentType)
-    {
-        return resolve(_appComponentPackage, componentType);
+        return packageName + "." + partialClassName.replace("/", ".");
     }
 
     public void setApplicationPackage(String packageName)

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java?view=auto&rev=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java Thu Sep 14 10:51:13 2006
@@ -0,0 +1,82 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.annotations.Component;
+import org.apache.tapestry.internal.ioc.IOCUtilities;
+import org.apache.tapestry.internal.util.InternalUtils;
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.model.MutableEmbeddedComponentModel;
+import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+import org.apache.tapestry.services.TransformConstants;
+
+/**
+ * Finds fields with the {@link org.apache.tapestry.annotations.Component} annotation and updates
+ * the model.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class ComponentWorker implements ComponentClassTransformWorker
+{
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        for (String fieldName : transformation.findFieldsWithAnnotation(Component.class))
+        {
+            Component annotation = transformation.getFieldAnnotation(fieldName, Component.class);
+
+            String id = annotation.id();
+
+            if (IOCUtilities.isBlank(id))
+                id = InternalUtils.stripMemberPrefix(fieldName);
+
+            String type = transformation.getFieldType(fieldName);
+
+            MutableEmbeddedComponentModel embedded = model.addEmbeddedComponent(id, type);
+
+            addParameters(embedded, annotation.parameters());
+
+            transformation.makeReadOnly(fieldName);
+
+            String body = String.format(
+                    "%s = (%s) %s.getEmbeddedComponent(\"%s\");",
+                    fieldName,
+                    type,
+                    transformation.getResourcesFieldName(),
+                    id);
+
+            transformation
+                    .extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, body);
+
+            transformation.claimField(fieldName, annotation);
+        }
+    }
+
+    private void addParameters(MutableEmbeddedComponentModel embedded, String[] parameters)
+    {
+        for (String parameter : parameters)
+        {
+            int colonx = parameter.indexOf("=");
+
+            // TODO: Malformed strings
+
+            String name = parameter.substring(0, colonx).trim();
+            String value = parameter.substring(colonx + 1).trim();
+
+            embedded.addParameter(name, value);
+        }
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Thu Sep 14 10:51:13 2006
@@ -120,14 +120,16 @@
     }
 
     public PageLoader buildPageLoader(@InjectService("PageElementFactory")
-    PageElementFactory pageElementFactory,
-
-    @InjectService("tapestry.BindingSource")
+    PageElementFactory pageElementFactory, @InjectService("tapestry.BindingSource")
     BindingSource bindingSource)
     {
         PageLoaderImpl service = new PageLoaderImpl(_componentTemplateSource, pageElementFactory,
                 bindingSource);
 
+        // Recieve invalidations when the class loader is discarded (due to a component class
+        // change).
+        // The notification is forwarded to the page loader's listeners.
+
         _componentInstantiatorSource.addInvalidationListener(service);
 
         return service;
@@ -150,7 +152,7 @@
     }
 
     /**
-     * The UpdateListenerHub provides events that other services used to check for invalidations.
+     * The UpdateListenerHub provides events that other services use to check for invalidations.
      * Such services usually are {@link org.apache.tapestry.internal.event.InvalidationEventHub}s,
      * and fire invalidation events to their listeners.
      */
@@ -226,7 +228,6 @@
 
                 initializer.initializeApplication(context);
             }
-
         };
 
         configuration.add("SetApplicationPackage", setApplicationPackage, "before:*.*");
@@ -237,7 +238,6 @@
             {
                 propertyAccess.clear();
             }
-
         };
 
         ApplicationInitializerFilter clearPropertyAccess = new ApplicationInitializerFilter()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java Thu Sep 14 10:51:13 2006
@@ -40,7 +40,16 @@
     ComponentPageElement newComponentElement(Page page, ComponentPageElement container, String id,
             String componentType, Location location);
 
-    ComponentPageElement newRootComponentElement(Page page, String componentType);
+    /**
+     * Creates a new root component for a page.
+     * 
+     * @param page
+     *            the page that will contain the root component
+     * @param className
+     *            the fully qualified class name of the root component
+     * @return the root page element
+     */
+    ComponentPageElement newRootComponentElement(Page page, String className);
 
     PageElement newRenderBodyElement(ComponentPageElement component);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoader.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoader.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoader.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoader.java Thu Sep 14 10:51:13 2006
@@ -33,11 +33,11 @@
     /**
      * Loads the page in the given locale.
      * 
-     * @param pageName
+     * @param pageClassName
      *            fully qualified class name of the root component of the page
      * @param locale
      *            the locale to load the page and its components in
      * @see Page#getLocale()
      */
-    Page loadPage(String pageName, Locale locale);
+    Page loadPage(String pageClassName, Locale locale);
 }

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=443428&r1=443427&r2=443428
==============================================================================
--- 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 Sep 14 10:51:13 2006
@@ -16,12 +16,15 @@
 
 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.InvalidationEvent;
 import org.apache.tapestry.events.InvalidationListener;
 import org.apache.tapestry.internal.InternalConstants;
+import org.apache.tapestry.internal.TapestryException;
 import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry.internal.parser.AttributeToken;
 import org.apache.tapestry.internal.parser.ComponentTemplate;
@@ -34,15 +37,14 @@
 import org.apache.tapestry.internal.structure.PageElement;
 import org.apache.tapestry.internal.structure.PageImpl;
 import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.model.EmbeddedComponentModel;
 import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.util.CollectionFactory;
 import org.apache.tapestry.util.IdAllocator;
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
 
 /**
- * This implementation is not threadsafe, and the "prethread" lifecycle should be used for the
- * PageLoader service.
- * 
  * @author Howard M. Lewis Ship
  */
 public class PageLoaderImpl extends InvalidationEventHubImpl implements PageLoader,
@@ -76,7 +78,12 @@
 
     private final List<ComponentPageElement> _componentQueue = newList();
 
-    public Page loadPage(String pageName, Locale locale)
+    /**
+     * 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
+     * allow the loader to work in parallel.
+     */
+    public synchronized Page loadPage(String pageName, Locale locale)
     {
         try
         {
@@ -105,11 +112,11 @@
         }
     }
 
-    private void loadRootComponent(String componentName)
+    private void loadRootComponent(String className)
     {
         ComponentPageElement rootComponent = _pageElementFactory.newRootComponentElement(
                 _page,
-                componentName);
+                className);
 
         _page.setRootElement(rootComponent);
 
@@ -127,19 +134,27 @@
         }
     }
 
+    /**
+     * 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 loadingComponent)
     {
         ComponentModel model = loadingComponent.getComponentModel();
 
-        ComponentTemplate template = _templateSource.getTemplate(
-                model.getComponentClassName(),
-                _locale);
+        String componentClassName = model.getComponentClassName();
+        ComponentTemplate template = _templateSource.getTemplate(componentClassName, _locale);
 
         // Perhaps when template is null, we should act as if the template
         // consists of just a render body element? Also, pages should (probably?)
         // be required to have a template. Somewhere in here, we need
         // to figure out how to search for a base-class template.
-
+        
+        // TODO: This needs some work, because the component may have defined embedded components
+        // that we need to log errors about.
+        
         if (template == null)
         {
             addRenderBodyElement(loadingComponent);
@@ -148,9 +163,21 @@
 
         // 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
@@ -240,8 +267,26 @@
                     if (embeddedId == null)
                         embeddedId = generateEmbeddedId(embeddedType, idAllocator);
 
-                    // TODO: Determine the type from from the ComponentModel if not present in the
-                    // template.
+                    EmbeddedComponentModel embeddedModel = model
+                            .getEmbeddedComponentModel(embeddedId);
+
+                    if (embeddedModel != null)
+                    {
+                        String modelType = embeddedModel.getComponentType();
+
+                        if (embeddedType != null)
+                            log.error(ServicesMessages.compTypeConflict(
+                                    embeddedId,
+                                    embeddedType,
+                                    modelType));
+
+                        embeddedType = modelType;
+                    }
+
+                    if (embeddedType == null)
+                        throw new TapestryException(ServicesMessages.noTypeForEmbeddedComponent(
+                                embeddedId,
+                                componentClassName), token, null);
 
                     ComponentPageElement newComponent = _pageElementFactory.newComponentElement(
                             _page,
@@ -252,9 +297,12 @@
 
                     add(loadingComponent, activeComponent, newComponent);
 
+                    if (embeddedModel != null)
+                        addParametersFromModel(embeddedModel, loadingComponent, newComponent);
+
                     // Make sure container knows about the new component.
 
-                    loadingComponent.addChild(newComponent);
+                    loadingComponent.addEmbeddedElement(newComponent);
 
                     // Remember that we have to load this new component and any of its
                     // subcomponents, later.
@@ -297,6 +345,27 @@
         }
     }
 
+    private void addParametersFromModel(EmbeddedComponentModel model,
+            ComponentResources 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,
+                    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 .
@@ -310,10 +379,18 @@
         return idAllocator.allocateId(embeddedType.substring(x + 1).toLowerCase());
     }
 
+    // This is for bindings from the template.
     private void addBindingToComponent(ComponentResources loadingComponent,
             ComponentPageElement component, AttributeToken token)
     {
         String name = token.getName();
+
+        // 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 (component.isBound(name))
+            return;
 
         // Default binding prefix inside templates is literal.
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RetainWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RetainWorker.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RetainWorker.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/RetainWorker.java Thu Sep 14 10:51:13 2006
@@ -14,8 +14,6 @@
 
 package org.apache.tapestry.internal.services;
 
-import java.util.List;
-
 import org.apache.tapestry.annotations.Retain;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.services.ClassTransformation;
@@ -35,9 +33,7 @@
      */
     public void transform(ClassTransformation transformation, MutableComponentModel model)
     {
-        List<String> fieldNames = transformation.findFieldsWithAnnotation(Retain.class);
-
-        for (String fieldName : fieldNames)
+        for (String fieldName : transformation.findFieldsWithAnnotation(Retain.class))
         {
             Retain annotation = transformation.getFieldAnnotation(fieldName, Retain.class);
 

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=443428&r1=443427&r2=443428
==============================================================================
--- 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 Sep 14 10:51:13 2006
@@ -154,4 +154,22 @@
     {
         return MESSAGES.get("comp-requires-id-or-type");
     }
+
+    static String compTypeConflict(String embeddedId, String templateType, String modelType)
+    {
+        return MESSAGES.format("comp-type-conflict", embeddedId, templateType, modelType);
+    }
+
+    static String noTypeForEmbeddedComponent(String embeddedId, String componentClassName)
+    {
+        return MESSAGES.format("no-type-for-embedded-component", embeddedId, componentClassName);
+    }
+
+    static String embeddedComponentsNotInTemplate(Collection<String> ids, String componentClassName)
+    {
+        return MESSAGES.format(
+                "embedded-components-not-in-template",
+                IOCUtilities.joinSorted(ids),
+                componentClassName);
+    }
 }

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=443428&r1=443427&r2=443428
==============================================================================
--- 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 Sep 14 10:51:13 2006
@@ -320,6 +320,8 @@
             String name = attributes.getLocalName(i);
             String value = attributes.getValue(i);
 
+            // TODO: Validate that the id is a reasonable string.
+            
             if (name.equals("id"))
             {
                 if (IOCUtilities.isNonBlank(value))

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- 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 Sep 14 10:51:13 2006
@@ -59,7 +59,7 @@
     void addParameter(String parameterName, Binding binding);
 
     /** Adds a child component to its container. The child's id must be unique within the container. */
-    void addChild(ComponentPageElement child);
+    void addEmbeddedElement(ComponentPageElement child);
 
     /** Invoked when the component should render its body. */
     void enqueueBeforeRenderBody(RenderQueue queue);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Thu Sep 14 10:51:13 2006
@@ -24,6 +24,7 @@
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.internal.TapestryException;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.internal.ioc.IOCUtilities;
 import org.apache.tapestry.internal.services.Instantiator;
 import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.model.ParameterModel;
@@ -222,7 +223,7 @@
         _bindings.put(parameterName, binding);
     }
 
-    public void addChild(ComponentPageElement child)
+    public void addEmbeddedElement(ComponentPageElement child)
     {
         if (_children == null)
             _children = newMap();
@@ -232,6 +233,16 @@
         // TODO: Check for conflicts!
 
         _children.put(childId, child);
+    }
+
+    public ComponentLifecycle getEmbeddedComponent(String embeddedId)
+    {
+        ComponentPageElement embeddedElement = IOCUtilities.get(_children, embeddedId);
+
+        if (embeddedElement == null)
+            throw new IllegalArgumentException(StructureMessages.noSuchComponent(this, embeddedId));
+
+        return embeddedElement.getComponent();
     }
 
     public String getId()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java Thu Sep 14 10:51:13 2006
@@ -35,4 +35,8 @@
                 .getComponentModel().getComponentClassName());
     }
 
+    static String noSuchComponent(ComponentPageElement parent, String embeddedId)
+    {
+        return MESSAGES.format("no-such-component", parent.getCompleteId(), embeddedId);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Thu Sep 14 10:51:13 2006
@@ -177,4 +177,16 @@
     {
         return newMock(ComponentPageElement.class);
     }
+
+    protected final void train_getComponent(ComponentPageElement element, ComponentLifecycle component)
+    {
+        element.getComponent();
+        setReturnValue(component);
+    }
+
+    protected final void train_getId(ComponentPageElement childElement, String id)
+    {
+        childElement.getId();
+        setReturnValue(id);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java Thu Sep 14 10:51:13 2006
@@ -52,4 +52,19 @@
 
     /** Return a single parameter model by parameter name, or null if the parameter is not defined. */
     ParameterModel getParameterModel(String parameterName);
+
+    /**
+     * Returns the ids of all embedded components defined within the component class (via the
+     * {@link org.apache.tapestry.annotations.Component} annotation).
+     */
+    List<String> getEmbeddedComponentIds();
+
+    /**
+     * Returns an embedded component.
+     * 
+     * @param componentId
+     *            the id of the embedded component
+     * @return the embedded component model, or null if no component exists with that id
+     */
+    EmbeddedComponentModel getEmbeddedComponentModel(String componentId);
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java?view=auto&rev=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java Thu Sep 14 10:51:13 2006
@@ -0,0 +1,38 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.model;
+
+import java.util.List;
+
+/**
+ * The model for a component embedded within another component, as defined by the
+ * {@link org.apache.tapestry.annotations.Component} annotation.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface EmbeddedComponentModel
+{
+    /** A unique id for the embedded component. */
+    String getId();
+
+    /** The type of the component. */
+    String getComponentType();
+
+    /** A sorted list of the names of all bound parameters. */
+    List<String> getParameterNames();
+
+    /** The value for each parameter, which will be interpreted as a binding expression. */
+    String getParameterValue(String parameterName);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java Thu Sep 14 10:51:13 2006
@@ -33,4 +33,15 @@
      *             if a parameter with the given name has already been defined for this model
      */
     void addParameter(String name, boolean required);
+
+    /**
+     * Defines a new embedded component.
+     * 
+     * @param id
+     *            the unique id for the embedded component, which must not already exist.
+     * @param type
+     *            the type of the component
+     * @return a mutable model allowing parameters to be set
+     */
+    MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type);
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableEmbeddedComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableEmbeddedComponentModel.java?view=auto&rev=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableEmbeddedComponentModel.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableEmbeddedComponentModel.java Thu Sep 14 10:51:13 2006
@@ -0,0 +1,26 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.model;
+
+/**
+ * A mutable version of {@link org.apache.tapestry.model.EmbeddedComponentModel} that allows the
+ * parameters to be incrementally stored.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface MutableEmbeddedComponentModel extends EmbeddedComponentModel
+{
+    void addParameter(String name, String value);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java Thu Sep 14 10:51:13 2006
@@ -34,8 +34,10 @@
      *            the component, as represented by its resources, for which a binding is to be
      *            created.
      * @param expression
-     * @param location TODO
+     * @param location
+     *            from which the binding was generate, or null if not known
      * @return the new binding instance
      */
-    Binding newBinding(String description, ComponentResources component, String expression, Location location);
+    Binding newBinding(String description, ComponentResources component, String expression,
+            Location location);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java Thu Sep 14 10:51:13 2006
@@ -39,7 +39,7 @@
      * @param expression
      *            the binding
      * @param location
-     *            location assigned to the binding
+     *            location assigned to the binding (or null if not known)
      * @return a binding
      */
     Binding newBinding(String description, ComponentResources component, String defaultPrefix,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java Thu Sep 14 10:51:13 2006
@@ -36,6 +36,7 @@
      * or annotation) into a fully qualified class name.
      * 
      * @param componentType
+     *            either a partial component class name, or a complete class name
      * @return fully qualified class name
      */
     String resolveComponentTypeToClassName(String componentType);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Thu Sep 14 10:51:13 2006
@@ -41,6 +41,7 @@
 import org.apache.tapestry.internal.services.ComponentInstantiatorSource;
 import org.apache.tapestry.internal.services.ComponentLifecycleMethodWorker;
 import org.apache.tapestry.internal.services.ComponentResourcesInjectionProvider;
+import org.apache.tapestry.internal.services.ComponentWorker;
 import org.apache.tapestry.internal.services.DefaultInjectionProvider;
 import org.apache.tapestry.internal.services.HTMLDispatcher;
 import org.apache.tapestry.internal.services.InfrastructureImpl;
@@ -425,7 +426,10 @@
      * Adds a number of standard component class transform workers:
      * <ul>
      * <li>Retain -- allows fields to retain their values between requests</li>
-     * <li>Parameter</li> -- identifies parameters based on the Parameter annotation</li>
+     * <li>Parameter</li> -- identifies parameters based on the
+     * {@link org.apache.tapestry.annotations.Parameter} annotation</li>
+     * <li>Component</li> -- identifies embedded components based on the
+     * {@link org.apache.tapestry.annotations.Component} annotation</li>
      * <li>UnclaimedField</li> -- identifies unclaimed fields and resets them to null/0/false at
      * the end of the request</li>
      * <li>SetupRender, BeginRender, etc. -- correspond to component render phases and annotations</li>
@@ -443,6 +447,7 @@
 
         configuration.add("Inject", new InjectWorker(objectProvider, locator, injectionProvider));
         configuration.add("Parameter", new ParameterWorker());
+        configuration.add("Component", new ComponentWorker());
 
         // Workers for the component rendering state machine methods; this is in typical
         // execution order.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties Thu Sep 14 10:51:13 2006
@@ -12,4 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-duplicate-parameter=Parameter '%s' of component %s is already defined.
\ No newline at end of file
+duplicate-parameter=Parameter '%s' of component %s is already defined.
+duplicate-parameter-value=A value for parameter '%s' of embedded component %s (of component class %s) has already been provided.
+duplicate-component-id=Embedded component '%s' has already been defined for component class %s.
\ No newline at end of file

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=443428&r1=443427&r2=443428
==============================================================================
--- 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 Sep 14 10:51:13 2006
@@ -40,4 +40,9 @@
 non-private-fields=Class %s contains field(s) (%s) that are not private. Tapestry will ignore these fields, even if they \
   have annotations. Runtime behavior, especially in production, may not be what you expect. \
   You should change these fields to private, and add accessor methods if needed.  
-comp-requires-id-or-type=The <comp> element requires either an id or a type attribute to be specified.
\ No newline at end of file
+comp-requires-id-or-type=The <comp> element requires either an id or a type attribute to be specified.
+comp-type-conflict=Embedded component '%s' provides a type attribute in the template ('%s') as well as in the component class ('%s'). \
+  You should not provide a type attribute in the template when defining an embedded component within the component class.
+no-type-for-embedded-component=Embedded component '%s' has no type. You should specify a type in the component template, \
+  or define the component inside class %s using the @Component annotation on a private instance variable.
+embedded-components-not-in-template=Embedded component(s) %s are defined within component class %s, but are not present in the component template.
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties Thu Sep 14 10:51:13 2006
@@ -12,4 +12,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-missing-parameters=Parameter(s) %s are required for %s, but have not been bound.
\ No newline at end of file
+missing-parameters=Parameter(s) %s are required for %s, but have not been bound.
+no-such-component=Component %s does not contain an embedded component with id '%s'.
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/component-classes.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/component-classes.apt?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/component-classes.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/component-classes.apt Thu Sep 14 10:51:13 2006
@@ -128,6 +128,56 @@
   the {{{.../apidocs/org/apache/tapestry/annotations/Parameter.html}Parameter}} annotation.
   
   
+{Embedded Components}
+
+  Components often contain other components.  Components inside another components template are called <embedded components>.  
+  The containing component's
+  {{{templates.html}template}} will contain \<comp\> element identifying where the the embedded components go.
   
+  You can define the type of component inside template, or you can create an instance variable for the component
+  and use the
+  {{{../apidocs/org/apache/tapestry/annotations/Component.html}Component}} annotation to define the component type
+  and parameters.  You should not provide the type attribute of the \<comp\> element, just the id attribute (if you do provide
+  the type attribute, Tapestry will log an error and use the type of the annotated field).
   
-  
\ No newline at end of file
+  Example:
+  
++---+
+@ComponentClass
+public class Countdown
+{
+    @Component(parameters =
+    { "start=literal:5", "end=literal:1", "value=count" })
+    private Loop _loop;  
+  
+    private int _count;
+
+    public int getCount()
+    {
+        return _count;
+    }
+
+    public void setCount(int count)
+    {
+        _count = count;
+    }
+}  
++---+
+
+  The above defines a component whose embedded id is "loop" (this id is derived from the name of the field).  The type
+  of component is Loop.  The start and end parameters of the Loop component are bound to literal values, and the value
+  parameter of the Loop component is bound to the count property of the Countdown component.
+  
+  Note that inside the component class, the default binding prefix is "prop:", whereas inside a component template,
+  the default binding prefix is "literal:".  To use literal values, you must prefix them, to keep Tapestry from looking
+  for a property named "5" or a property named "1".
+  
+  You may specify additional parameters inside the component template, but parameters in the component class
+  take precendence.  
+  
+  <<TODO: May want a more complex check; what if user uses prop: in the template and there's a conflict?>>
+  
+  You may override the id using the id() attribute of the Component annotation.
+  
+  If you define a component in the component class, and there is no corresponding \<comp\> element in the template,
+  Tapestry will log an error.
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/conf.apt Thu Sep 14 10:51:13 2006
@@ -38,7 +38,7 @@
     </filter>
     <filter-mapping>
         <filter-name>app</filter-name>
-        <url-pattern>/</url-pattern>
+        <url-pattern>/*</url-pattern>
     </filter-mapping>
 </web-app>
 +----+  

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt Thu Sep 14 10:51:13 2006
@@ -16,44 +16,69 @@
   onto a private field.
   
   The component listed below is a looping component; it renders its body
-  a number of times, defined by its min and max parameters (which set the boundaries
-  of the loop):
+  a number of times, defined by its start and end parameters (which set the boundaries
+  of the loop).  The component can update a value parameter bound to a property of its container,
+  it will automatically count up or down depending on whether start or end is larger.
   
 +---+
-package org.example.components;
+package org.apache.tapestry.integration.app1.components;
 
+import org.apache.tapestry.annotations.AfterRender;
 import org.apache.tapestry.annotations.ComponentClass;
 import org.apache.tapestry.annotations.Parameter;
-import org.apache.tapestry.annotations.AfterRender;
 import org.apache.tapestry.annotations.SetupRender;
 
 @ComponentClass
 public class Loop
 {
     @Parameter
-    private int _min = 1;
+    private int _start = 1;
 
     @Parameter(required = true)
-    private int _max;
+    private int _end;
 
     @Parameter
     private int _value;
 
+    private boolean _increment;
+
     @SetupRender
     void initializeValue()
     {
-        _value = _min;
+        if (_start < _end)
+        {
+            _value = _start;
+            _increment = true;
+        }
+        else
+        {
+            _value = _start;
+            _increment = false;
+        }
     }
 
     @AfterRender
-    boolean increment()
+    boolean next()
     {
-        int newValue = _value + 1;
+        if (_increment)
+        {
+            int newValue = _value + 1;
 
-        if (newValue <= _max)
+            if (newValue <= _end)
+            {
+                _value = newValue;
+                return true; // re-render body
+            }
+        }
+        else
         {
-            _value = newValue;
-            return true;
+            int newValue = _value - 1;
+
+            if (newValue >= _end)
+            {
+                _value = newValue;
+                return true; // re-render body
+            }
         }
 
         return false;
@@ -62,8 +87,7 @@
 +---+
   
   The name of the parameter is derived from the field name (by stripping leading "_" and "$" characters).
-  Here, the parameter names are "min", "max" and "value".  You can override the name of the parameter
-  using the name() attribute of the Parameter annotation.
+  Here, the parameter names are "start", "end" and "value".  
   
 Binding Parameters
 
@@ -72,17 +96,23 @@
   
 +---+
 <t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-    <p> Merry Christmas: <t:comp type="Loop" max="3"> Ho! </t:comp>
+    <p> Merry Christmas: <t:comp type="Loop" end="3"> Ho! </t:comp>
     </p>
 </t:comp>
 +---+  
   
-  Inside the \<comp\> element, the max attribute is used to <bind> the max parameter of the
+  Inside the \<comp\> element, the end attribute is used to <bind> the end parameter of the
   Loop component.  Here, it is being bound to the string value "3", which is automatically
   coerced by Tapestry into the int value, 3.
     
   Any number of parameters may be bound this way.
   
+  Component parameters may also be bound using the
+  {{{component-classes.html#Embedded Components}@Component annotation}} inside the component class.
+  
+  Where conflicts occur, the component takes precendence over parameter bindings
+  in the template.
+    
 Binding Expressions
 
   The value inside the template, "3" in the previous example, is a <binding expression>.
@@ -97,9 +127,11 @@
 *------------+----------------------------------------------------------------------------------+
 | literal:   | A literal string. The default inside a component template.                       |
 *------------+----------------------------------------------------------------------------------+
-| prop:     | The name of a property of the containing component to read or update.            |
+| prop:      | The name of a property of the containing component to read or update.            |
 *------------+----------------------------------------------------------------------------------+
 
+  Inside component classes, the default binding prefix is "prop:".
+
   <<Note: More prefixes are forthcoming.>>
     
 Parameters Are Bi-Directional
@@ -111,9 +143,9 @@
   
 +---+
 <t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-    <p> Counting: 
-        <t:comp type="Loop" max="5" value="prop:index"> 
-          #${index} ...  
+    <p> Countdown:
+        <t:comp type="Loop" start="5" end="1" value="prop:index"> 
+          ${index} ...  
         </t:comp>
     </p>
 </t:comp>
@@ -125,12 +157,12 @@
   will look something like:
   
 +---+
-  <p> Counting: 1 ... 2 ... 3 ... 4 ... 5 ... </p>
+  <p> Countdown: 5 ... 4 ... 3 ... 2 ... 1 ... </p>
 +---+
 
   (Though the whitespace will be quite different.)  
   
-  The relevant part is that components can read fixed values or live properties of their
+  The relevant part is that components can read fixed values, or live properties of their
   container, and can <change> properties of their container as well.
   
 Required Parameters

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/templates.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/templates.apt?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/templates.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/templates.apt Thu Sep 14 10:51:13 2006
@@ -70,7 +70,8 @@
   If the id attribute is ommitted, Tapestry will assign a unique id for the element.
   
   The type attribute is optional; when not specified, Tapestry will expect the class to define
-  the type of the component (with a @Component annotation).  <<Not yet implemented.>>
+  the type of the component (with 
+  {{{component-classes.html#Embedded Components}a @Component annotation}}}).
   
   Additional attributes are used to {{{parameters.html}bind parameters of the component}}.  
   When parameters are bound

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html Thu Sep 14 10:51:13 2006
@@ -16,6 +16,12 @@
                 <li>
                     <a href="InjectPage.html">Inject Page</a>
                 </li>
+                <li>
+                    <a href="Countdown.html">Countdown Page</a>
+                </li>
+            <li>
+                <a href="ParameterConflict.html">Template Overriden by Class Page</a>
+            </li>
             </ul>
         </p>
         <p> Feast your eyes: </p>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Thu Sep 14 10:51:13 2006
@@ -142,4 +142,32 @@
                 .contains("<Proxy for tapestry.BindingSource(org.apache.tapestry.services.BindingSource)>"));
     }
 
+    @Test
+    public void app1_embedded_components()
+    {
+        _selenium.open(BASE_URL);
+
+        _selenium.click("link=Countdown Page");
+        _selenium.waitForPageToLoad(PAGE_LOAD_TIMEOUT);
+
+        String body = _selenium.getBodyText();
+
+        assertTrue(body.contains("5 4 3 2 1"));
+
+        assertTrue(body
+                .contains("Brought to you by the org.apache.tapestry.integration.app1.components.Loop"));
+    }
+
+    @Test
+    public void app1_template_overridden()
+    {
+        _selenium.open(BASE_URL);
+
+        _selenium.click("link=Template Overriden by Class Page");
+        _selenium.waitForPageToLoad(PAGE_LOAD_TIMEOUT);
+
+        String body = _selenium.getBodyText();
+
+        assertTrue(body.contains("Output: ClassValue"));
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Loop.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Loop.java?view=diff&rev=443428&r1=443427&r2=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Loop.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Loop.java Thu Sep 14 10:51:13 2006
@@ -23,29 +23,53 @@
 public class Loop
 {
     @Parameter
-    private int _min = 1;
+    private int _start = 1;
 
     @Parameter(required = true)
-    private int _max;
+    private int _end;
 
     @Parameter
     private int _value;
 
+    private boolean _increment;
+
     @SetupRender
     void initializeValue()
     {
-        _value = _min;
+        if (_start < _end)
+        {
+            _value = _start;
+            _increment = true;
+        }
+        else
+        {
+            _value = _start;
+            _increment = false;
+        }
     }
 
     @AfterRender
-    boolean increment()
+    boolean next()
     {
-        int newValue = _value + 1;
+        if (_increment)
+        {
+            int newValue = _value + 1;
 
-        if (newValue <= _max)
+            if (newValue <= _end)
+            {
+                _value = newValue;
+                return true; // re-render body
+            }
+        }
+        else
         {
-            _value = newValue;
-            return true;
+            int newValue = _value - 1;
+
+            if (newValue >= _end)
+            {
+                _value = newValue;
+                return true; // re-render body
+            }
         }
 
         return false;

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Countdown.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Countdown.java?view=auto&rev=443428
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Countdown.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Countdown.java Thu Sep 14 10:51:13 2006
@@ -0,0 +1,66 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.Component;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.integration.app1.components.Loop;
+import org.apache.tapestry.integration.app1.components.Output;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+@ComponentClass
+public class Countdown
+{
+    @Component(parameters =
+    { "start=max", "end=min", "value=count" })
+    private Loop _loop;
+
+    @SuppressWarnings("unused")
+    @Component(parameters = "value=count")
+    private Output _output;
+
+    private int _count;
+
+    public int getCount()
+    {
+        return _count;
+    }
+
+    public void setCount(int count)
+    {
+        _count = count;
+    }
+
+    // These are just necessary until we get parameter type coercions working.
+
+    public int getMax()
+    {
+        return 5;
+    }
+
+    public int getMin()
+    {
+        return 1;
+    }
+
+    // Just needed until component: binding prefix is added.
+    
+    public Loop getLoop()
+    {
+        return _loop;
+    }
+}