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

svn commit: r744608 [1/2] - in /tapestry/tapestry5/trunk: src/site/apt/ src/site/apt/guide/ tapestry-core/src/main/java/org/apache/tapestry5/ tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ tapestry-core/src/main/java/org/apache/ta...

Author: hlship
Date: Sun Feb 15 04:44:12 2009
New Revision: 744608

URL: http://svn.apache.org/viewvc?rev=744608&view=rev
Log:
TAP5-487: Easier way to expose parameters of an embedded component in a containing component

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerSource.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentName.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinder.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinderImpl.java
      - copied, changed from r744203, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinder.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/PublishParametersDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/Publish1.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/Publish2.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/Publish3.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishParametersDemo.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Publish1.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Publish2.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Publish3.tml
Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InheritedBinding.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InheritedBindingTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java
Modified:
    tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt
    tapestry/tapestry5/trunk/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssembler.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssembler.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageElementFactory.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageElementFactoryImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageLoader.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PagePoolImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElement.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ActionViaLinkDemo.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/TapestryInternalUtilsTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageElementFactoryImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PagePoolImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/test/InternalBaseTestCase.java

Modified: tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt Sun Feb 15 04:44:12 2009
@@ -486,4 +486,24 @@
   builds around your class.  In any case, once the resources are injected,
   they can be queried.
  
-   
\ No newline at end of file
+Publishing Parameters
+
+  Often when creating new components from existing components, you want to expose some of the
+  functionality of the embedded component, in the form of exposing parameters of the embedded components
+  as parameters of the outer component.
+
+  In Tapestry 5.0, you would define a parameter of the outer component, and use the
+  "inherit:" binding prefix to connect the inner component's parameter to the outer component's parameter.
+  This is somewhat clumsy, as it involves creating an otherwise unused field just for the parameter; in practice
+  it also leads to duplication of the documentation of the parameter.
+
+  In Tapestry 5.1, you may use the publishParameters attribute of the
+  {{{../../apidocs/org/apache/tapestry5/annotations/Component.html}Component}}} annotation. List one
+  or more parameters seperated by commas: those parameters of the inner/embedded component become
+  parameters of the outer component.  You should not define a parameter field in the outer component.
+
+  There are still cases where you want to use the "inherit:" binding prefix. For example, if you have
+  several components that need to share a parameter, then you must do it the Tapestry 5.0 way: a true parameter
+  on the outer component, and "inherit:" bindings on the embedded components. You can follow a similar pattern
+  to rename a parameter in the outer component.
+

Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Sun Feb 15 04:44:12 2009
@@ -32,6 +32,33 @@
 
  Tapestry is released under the Apache Software Licence 2.0.
 
+New And Of Note
+
+  * Some significant performance improvements over Tapestry 5.0.18: the time to initially load a page,
+    and the time to render a page have decreased.
+
+  * Tapestry IoC services can now be easily <{{{tapestry-ioc/advice.html}advised}}> as well as
+   <{{{tapestry-ioc/decorator.html}decorated}}> (both of these refer to Aspect Oriented Techniques applied to
+    Tapestry IoC services).
+
+  * Tapestry Services can now be injected into Spring Beans, when using the Tapestry/Spring integration library.
+
+  * Tapestry now {{{guide/compress.html}compresses}} responses for clients that support compression.
+    Context and classpath assets
+    are now handled uniformly: versioned URLs, far-future expiration headers, and GZIP compression where applicable.
+
+  * Ordered and mapped configurations can now have overrides.
+
+  * Property expressions have been improved: You can now invoke methods with parameters, or create a list (very useful for link contexts).
+
+  * IoC Service contributions may now be made in terms of classes (that are automatically instantiated) as well as
+    instances.
+
+  * A simpler method of {{{apidocs/org/apache/tapestry5/ioc/services/ServiceOverride}overriding built-in services}}
+    has been added.
+
+  []
+  
 Roadmap
 
   Now that that 5.0 release is <finally> out and available, work is starting on a 5.1 release.
@@ -79,29 +106,7 @@
 | {{{http://code.google.com/p/tapestry5-treegrid/}tapestry5-treegrid}} | Gabriel Landais |  Combination tree navigation and data grid, based on sstree. |
 *--+--+--+
 
-New And Of Note
-
-  * Tapestry IoC services can now be easily <{{{tapestry-ioc/advice.html}advised}}> as well as
-   <{{{tapestry-ioc/decorator.html}decorated}}> (both of these refer to Aspect Oriented Techniques applied to
-    Tapestry IoC services).
-
-  * Tapestry Services can now be injected into Spring Beans, when using the Tapestry/Spring integration library.
-
-  * Tapestry now {{{guide/compress.html}compresses}} responses for clients that support compression.
-    Context and classpath assets
-    are now handled uniformly: versioned URLs, far-future expiration headers, and GZIP compression where applicable.
 
-  * Ordered and mapped configurations can now have overrides.
-
-  * Property expressions have been improved: You can now invoke methods with parameters, or create a list (very useful for link contexts).
-
-  * IoC Service contributions may now be made in terms of classes (that are automatically instantiated) as well as
-    instances.
-
-  * A simpler method of {{{apidocs/org/apache/tapestry5/ioc/services/ServiceOverride}overriding built-in services}}
-    has been added.
-
-  []
 
 Adaptive API
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java Sun Feb 15 04:44:12 2009
@@ -34,15 +34,14 @@
 
     /**
      * Return a string consisting the concatinated ids of all containing components, separated by periods. In addition,
-     * nested ids are always all lower case. I.e., "foo.bar.baz". Returns null for the root component of a  page.
+     * nested ids are always all lower case. I.e., "foo.bar.baz". Returns null for the root component of a page.
      */
     String getNestedId();
 
 
     /**
      * Returns a string consisting of the logical name of the containing page, and the {@link #getNestedId() nested id}
-     * of this component, separated by a colon. I.e., "MyPage:foo.bar.baz". For a page, returns just the page's logical
-     * name.
+     * of this component, separated by a colon. I.e., "MyPage:foo.bar.baz". For a page, returns just the page's name.
      * <p/>
      * This value is often used to obtain an equivalent component instance in a later request.
      *

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java Sun Feb 15 04:44:12 2009
@@ -45,14 +45,6 @@
 @SupportsInformalParameters
 public class BeanEditForm implements ClientElement, FormValidationControl
 {
-    /**
-     * If true (the default), then the JavaScript will be added to position the cursor into the form. The field to
-     * receive focus is the first rendered field that is in error, or required, or present (in that order of priority).
-     *
-     * @see org.apache.tapestry5.corelib.components.Form#autofocus
-     */
-    @Parameter
-    private boolean autofocus;
 
     /**
      * The text label for the submit button of the form, by default "Create/Update".
@@ -103,23 +95,8 @@
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
     private String reorder;
 
-    /**
-     * May be bound, to override the Form's default for clientValidation.
-     */
-    @Parameter
-    private boolean clientValidation;
-
-    /**
-     * Binding the zone parameter will cause the form submission to be handled as an Ajax request that updates the
-     * indicated zone.  Often a BeanEditForm will update the same zone that contains it.
-     */
-    @Parameter(defaultPrefix = BindingConstants.LITERAL)
-    private String zone;
-
-    @Component(parameters = { "clientValidation=inherit:clientValidation",
-            "validationId=componentResources.id",
-            "autofocus=inherit:autofocus",
-            "zone=inherit:zone" })
+    @Component(parameters =
+            "validationId=componentResources.id", publishParameters = "clientValidation,autofocus,zone")
     private Form form;
 
     /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java Sun Feb 15 04:44:12 2009
@@ -41,8 +41,7 @@
  * editing properties of each row. This is currently workable but less than ideal -- if the order of rows provided by
  * the {@link org.apache.tapestry5.grid.GridDataSource} changes between render and form submission, then there's the
  * possibility that data will be applied to the wrong server-side objects. In general, when using Grid and Form
- * together, you want to provide the Grid with a {@link org.apache.tapestry5.PrimaryKeyEncoder} (via the encoder
- * parameter).
+ * together, you want to provide the Grid with a {@link org.apache.tapestry5.ValueEncoder} (via the encoder parameter).
  *
  * @see org.apache.tapestry5.beaneditor.BeanModel
  * @see org.apache.tapestry5.services.BeanModelSource
@@ -89,13 +88,7 @@
     private Object row;
 
     /**
-     * Optional output parameter used to identify the index (from zero) of the row being rendered.
-     */
-    @Parameter
-    private int rowIndex;
-
-    /**
-     * Optional output parmater used to identify the index of the column being rendered.
+     * Optional output parmeter used to identify the index of the column being rendered.
      */
     @Parameter
     private int columnIndex;
@@ -161,31 +154,6 @@
     @Parameter(value = "block:empty", defaultPrefix = BindingConstants.LITERAL)
     private Block empty;
 
-
-    /**
-     * If true, then the CSS class on each &lt;TD&gt; and &lt;TH&gt; cell will be omitted, which can reduce the amount
-     * of output from the component overall by a considerable amount. Leave this as false, the default, when you are
-     * leveraging the CSS to customize the look and feel of particular columns.
-     */
-    @Parameter
-    private boolean lean;
-
-    /**
-     * If true and the Grid is enclosed by a Form, then the normal state persisting logic is turned off. Defaults to
-     * false, enabling state persisting within Forms. If a Grid is present for some reason within a Form, but does not
-     * contain any form control components (such as {@link TextField}), then binding volatile to false will reduce the
-     * amount of client-side state that must be persisted.
-     */
-    @Parameter(name = "volatile")
-    private boolean volatileState;
-
-    /**
-     * The CSS class for the tr element for each data row. This can be used to highlight particular rows, or cycle
-     * between CSS values (for the "zebra effect"). If null or not bound, then no particular CSS class value is used.
-     */
-    @Parameter(cache = false)
-    private String rowClass;
-
     /**
      * CSS class for the &lt;table&gt; element.  In addition, informal parameters to the Grid are rendered in the table
      * element.
@@ -203,14 +171,6 @@
     private boolean inPlace;
 
     /**
-     * Changes how state is recorded into the form to store the {@linkplain org.apache.tapestry5.ValueEncoder#toClient(Object)
-     * client value} for each row (rather than the index), and restore the {@linkplain
-     * org.apache.tapestry5.ValueEncoder#toValue(String)row values} from the client value.
-     */
-    @Parameter
-    private ValueEncoder encoder;
-
-    /**
      * The name of the psuedo-zone that encloses the Grid.
      */
     @Property(write = false)
@@ -247,16 +207,12 @@
 
     @Component(
             parameters = {
-                    "rowIndex=inherit:rowIndex",
                     "columnIndex=inherit:columnIndex",
-                    "rowClass=inherit:rowClass",
                     "rowsPerPage=rowsPerPage",
                     "currentPage=currentPage",
                     "row=row",
-                    "overrides=overrides",
-                    "volatile=inherit:volatile",
-                    "encoder=inherit:encoder",
-                    "lean=inherit:lean" })
+                    "overrides=overrides" },
+            publishParameters = "rowIndex,rowClass,volatile,encoder,lean")
     private GridRows rows;
 
     @Component(parameters = {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java Sun Feb 15 04:44:12 2009
@@ -14,7 +14,6 @@
 
 package org.apache.tapestry5.internal;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.tapestry5.*;
 import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.Resource;
@@ -25,7 +24,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.math.BigInteger;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
@@ -45,6 +43,8 @@
 
     private static final int BUFFER_SIZE = 5000;
 
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
     /**
      * Capitalizes the string, and inserts a space before each upper case character (or sequence of upper case
      * characters). Thus "userId" becomes "User Id", etc. Also, converts underscore into space (and capitalizes the
@@ -389,8 +389,8 @@
      */
     public static String[] splitAtCommas(String value)
     {
-        if (value == null)
-            return new String[0];
+        if (InternalUtils.isBlank(value))
+            return EMPTY_STRING_ARRAY;
 
         return COMMA_PATTERN.split(value.trim());
     }
@@ -421,26 +421,6 @@
         out.flush();
     }
 
-    /**
-     * Encodes a long in Base64. Strips off the trailing "=" padding characters (if present).
-     *
-     * @param input value to encode
-     * @return encoded as Base64
-     * @since 5.1.1.0
-     */
-    public static String toBase64(long input)
-    {
-        BigInteger big = new BigInteger(Long.toString(input));
-
-        byte[] encoded = Base64.encodeBase64(big.toByteArray());
-
-        String asString = new String(encoded);
-
-        int lastx = asString.indexOf('=');
-
-        return lastx > 0 ? asString.substring(0, lastx) : asString;
-    }
-
     public static boolean isEqual(EventContext left, EventContext right)
     {
         if (left == right) return true;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssembler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssembler.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssembler.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssembler.java Sun Feb 15 04:44:12 2009
@@ -19,6 +19,7 @@
 import org.apache.tapestry5.internal.structure.Page;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.model.EmbeddedComponentModel;
 
 /**
  * Encapsulates a series of actions that are used to assemble a new page instance (or a comoponent within the page).
@@ -41,13 +42,15 @@
      * Assembles a component embedded within another component, leaving the new component on the {@link
      * org.apache.tapestry5.internal.pageload.PageAssembly#createdElement} stack.
      *
-     * @param pageAssembly holds dynamic state while assembling the comopnent
-     * @param embeddedId   the unique id for the component within its container
-     * @param elementName  element name in the template for the component (or null if defined via a Tapestry namespaced
-     *                     element)
-     * @param location     location of the embedded component in its container's template
+     * @param pageAssembly      holds dynamic state while assembling the comopnent
+     * @param embeddedAssembler
+     * @param embeddedId        the unique id for the component within its container
+     * @param elementName       element name in the template for the component (or null if defined via a Tapestry
+     *                          namespaced element)
+     * @param location          location of the embedded component in its container's template
      */
-    void assembleEmbeddedComponent(PageAssembly pageAssembly, String embeddedId, String elementName,
+    void assembleEmbeddedComponent(PageAssembly pageAssembly, EmbeddedComponentAssembler embeddedAssembler,
+                                   String embeddedId, String elementName,
                                    Location location);
 
     /**
@@ -70,4 +73,32 @@
      * @return unique id based on the type
      */
     String generateEmbeddedId(String componentType);
+
+    /**
+     * Creates an assembler for an embedded component within this component. Does some additional tracking of published
+     * parameters.
+     *
+     * @param embeddedId         unique id for the embedded component
+     * @param componentClassName class name to instantiate
+     * @param embeddedModel      model defining how the component is used (may be null)
+     * @param mixins             mixins for the component (as defined in the template)
+     * @param location           location of the component (i.e., it's location in the container template)
+     * @return assembler for the component
+     */
+    EmbeddedComponentAssembler createEmbeddedAssembler(String embeddedId, String componentClassName,
+                                                       EmbeddedComponentModel embeddedModel,
+                                                       String mixins,
+                                                       Location location);
+
+    /**
+     * Finds a binder for a published parameter, or returns null. That is, if the parameter name matches the name of a
+     * parameter of an emebdded components of this component, returns a parameter binder for the parameter. The caller
+     * will pass the {@link org.apache.tapestry5.internal.structure.ComponentPageElement} that corresponds to this
+     * component to the binder and the binder will, internally, redirect to the correct embedded ComponentPageElement.
+     *
+     * @param parameterName simple (unqualified) name of parameter
+     * @return binder, or null if the parameter name does not correspond to a published parameter of an embedded
+     *         component
+     */
+    ParameterBinder getBinder(String parameterName);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java Sun Feb 15 04:44:12 2009
@@ -14,7 +14,9 @@
 
 package org.apache.tapestry5.internal.pageload;
 
+import org.apache.tapestry5.Binding;
 import org.apache.tapestry5.internal.parser.ComponentTemplate;
+import org.apache.tapestry5.internal.services.ComponentInstantiatorSource;
 import org.apache.tapestry5.internal.services.Instantiator;
 import org.apache.tapestry5.internal.structure.*;
 import org.apache.tapestry5.ioc.Location;
@@ -23,25 +25,51 @@
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.model.EmbeddedComponentModel;
 import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.services.ComponentClassResolver;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 class ComponentAssemblerImpl implements ComponentAssembler
 {
+    private final ComponentAssemblerSource assemblerSource;
+
+    private final ComponentInstantiatorSource instantiatorSource;
+
+    private final ComponentClassResolver componentClassResolver;
+
     private final Instantiator instantiator;
 
+    private final Locale locale;
+
     private final ComponentPageElementResources resources;
 
     private final List<PageAssemblyAction> actions = CollectionFactory.newList();
 
     private final IdAllocator allocator = new IdAllocator();
 
-    public ComponentAssemblerImpl(Instantiator instantiator, ComponentPageElementResources resources)
-    {
+    private final Map<String, String> publishedParameterToEmbeddedId = CollectionFactory.newCaseInsensitiveMap();
+
+    // Doesn't have to be case-insensitive, because the embeddedIds are always known alues from
+    // publishedParameterToEmbeddedId.
+
+    private final Map<String, String> embeddedIdToComponentClassName = CollectionFactory.newMap();
+
+    public ComponentAssemblerImpl(ComponentAssemblerSource assemblerSource,
+                                  ComponentInstantiatorSource instantiatorSource,
+                                  ComponentClassResolver componentClassResolver,
+                                  Instantiator instantiator,
+                                  ComponentPageElementResources resources, Locale locale)
+    {
+        this.assemblerSource = assemblerSource;
+        this.instantiatorSource = instantiatorSource;
+        this.componentClassResolver = componentClassResolver;
         this.instantiator = instantiator;
         this.resources = resources;
+        this.locale = locale;
     }
 
     public ComponentPageElement assembleRootComponent(Page page)
@@ -52,7 +80,17 @@
         {
             ComponentPageElement newElement = new ComponentPageElementImpl(pageAssembly.page, instantiator, resources);
 
-            buildRecursively(pageAssembly, newElement);
+            pageAssembly.componentName.push(new ComponentName(pageAssembly.page.getName()));
+
+            pageAssembly.page.addLifecycleListener(newElement);
+
+            addRootComponentMixins(newElement);
+
+            pushNewElement(pageAssembly, newElement);
+
+            runActions(pageAssembly);
+
+            popNewElement(pageAssembly);
 
             for (PageAssemblyAction action : pageAssembly.deferred)
             {
@@ -64,22 +102,54 @@
         catch (RuntimeException ex)
         {
             throw new RuntimeException(String.format("Exception assembling root component of page %s: %s",
-                                                     pageAssembly.page.getLogicalName(),
+                                                     pageAssembly.page.getName(),
                                                      InternalUtils.toMessage(ex)),
                                        ex);
         }
     }
 
-    public void assembleEmbeddedComponent(PageAssembly pageAssembly, String embeddedId, String elementName,
+    private void addRootComponentMixins(ComponentPageElement element)
+    {
+        for (String className : instantiator.getModel().getMixinClassNames())
+        {
+            Instantiator mixinInstantiator = instantiatorSource.getInstantiator(className);
+
+            element.addMixin(InternalUtils.lastTerm(className), mixinInstantiator);
+        }
+    }
+
+    public void assembleEmbeddedComponent(PageAssembly pageAssembly, EmbeddedComponentAssembler embeddedAssembler,
+                                          String embeddedId, String elementName,
                                           Location location)
     {
         ComponentPageElement container = pageAssembly.activeElement.peek();
 
         try
         {
-            ComponentPageElement newElement = container.newChild(embeddedId, elementName, instantiator, location);
+            ComponentName containerName = pageAssembly.componentName.peek();
+
+            ComponentName embeddedName = containerName.child(embeddedId.toLowerCase());
+
+            pageAssembly.componentName.push(embeddedName);
+
+            ComponentPageElement newElement = container.newChild(embeddedId,
+                                                                 embeddedName.nestedId,
+                                                                 embeddedName.completeId,
+                                                                 elementName,
+                                                                 instantiator,
+                                                                 location);
+
+            pageAssembly.page.addLifecycleListener(newElement);
+
+            pushNewElement(pageAssembly, newElement);
+
+            embeddedAssembler.addMixinsToElement(newElement);
+
+            runActions(pageAssembly);
+
+            popNewElement(pageAssembly);
 
-            buildRecursively(pageAssembly, newElement);
+            pageAssembly.componentName.pop();
         }
         catch (RuntimeException ex)
         {
@@ -93,17 +163,6 @@
         }
     }
 
-    private void buildRecursively(PageAssembly pageAssembly, ComponentPageElement newElement)
-    {
-        pageAssembly.page.addLifecycleListener(newElement);
-
-        pushNewElement(pageAssembly, newElement);
-
-        runActions(pageAssembly);
-
-        popNewElement(pageAssembly);
-    }
-
     private void pushNewElement(PageAssembly pageAssembly, final ComponentPageElement componentElement)
     {
         // This gets popped after all actions have executed.
@@ -192,6 +251,108 @@
         return allocator.allocateId(baseId);
     }
 
+    public EmbeddedComponentAssembler createEmbeddedAssembler(String embeddedId, String componentClassName,
+                                                              EmbeddedComponentModel embeddedModel, String mixins,
+                                                              Location location)
+    {
+        EmbeddedComponentAssemblerImpl embedded = new EmbeddedComponentAssemblerImpl(assemblerSource,
+                                                                                     instantiatorSource,
+                                                                                     componentClassResolver,
+                                                                                     componentClassName,
+                                                                                     locale, embeddedModel,
+                                                                                     mixins,
+                                                                                     location);
+
+        if (embeddedModel != null)
+        {
+            ComponentModel embeddedComponentModel = instantiatorSource.getInstantiator(componentClassName).getModel();
+
+            for (String publishedParameterName : embeddedModel.getPublishedParameters())
+            {
+                String existingEmbeddedId = publishedParameterToEmbeddedId.get(publishedParameterName);
+
+                if (existingEmbeddedId != null)
+                {
+                    String message = String.format(
+                            "Parameter '%s' can not be published by embedded component '%s' as it has already been published by embedded component '%s'.",
+                            publishedParameterName, embedded, existingEmbeddedId);
+
+                    throw new TapestryException(message, location, null);
+                }
+
+                if (embeddedComponentModel.getParameterModel(publishedParameterName) == null)
+                {
+                    String message = String.format(
+                            "Component %s does not include a parameter named '%s' to publish. Possible parameters: %s.",
+                            componentClassName, publishedParameterName,
+                            InternalUtils.joinSorted(embeddedComponentModel.getParameterNames()));
+
+                    throw new TapestryException(message, location, null);
+                }
+
+                publishedParameterToEmbeddedId.put(publishedParameterName, embeddedId);
+            }
+
+        }
+
+        embeddedIdToComponentClassName.put(embeddedId, componentClassName);
+
+        return embedded;
+    }
+
+    public ParameterBinder getBinder(final String parameterName)
+    {
+        final String embeddedId = publishedParameterToEmbeddedId.get(parameterName);
+
+        if (embeddedId == null) return null;
+
+        String componentClassName = embeddedIdToComponentClassName.get(embeddedId);
+
+        final ComponentAssembler embeddedAssembler = assemblerSource.getAssembler(componentClassName, locale);
+
+        final ParameterBinder embeddedBinder = embeddedAssembler.getBinder(parameterName);
+
+        // The complex case: a re-publish!  Yes you can go deep here if you don't
+        // value your sanity!
+
+        if (embeddedBinder != null)
+        {
+            return new ParameterBinder()
+            {
+                public void bind(ComponentPageElement element, Binding binding)
+                {
+                    ComponentPageElement subelement = element.getEmbeddedElement(embeddedId);
+
+                    embeddedBinder.bind(subelement, binding);
+                }
+
+                public String getDefaultBindingPrefix(String metaDefault)
+                {
+                    return embeddedBinder.getDefaultBindingPrefix(metaDefault);
+                }
+            };
+        }
+
+        // The simple case, publishing a parameter of a subcomponent as if it were a parameter
+        // of this component.
+
+        return new ParameterBinder()
+        {
+            public void bind(ComponentPageElement element, Binding binding)
+            {
+                ComponentPageElement subelement = element.getEmbeddedElement(embeddedId);
+
+                subelement.bindParameter(parameterName, binding);
+            }
+
+            public String getDefaultBindingPrefix(String metaDefault)
+            {
+                return embeddedAssembler.getModel().getParameterModel(parameterName).getDefaultBindingPrefix();
+            }
+        };
+    }
+
+
     @Override
     public String toString()
     {

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerSource.java?rev=744608&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerSource.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerSource.java Sun Feb 15 04:44:12 2009
@@ -0,0 +1,29 @@
+// Copyright 2009 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.tapestry5.internal.pageload;
+
+import java.util.Locale;
+
+/**
+ * Provides access to {@link org.apache.tapestry5.internal.pageload.ComponentAssembler}s (this is used by the assemblers
+ * to find related assemblers).
+ */
+public interface ComponentAssemblerSource
+{
+    /**
+     * Gets (and possibly creates) a component assembler for the indicated class name and locale.
+     */
+    public ComponentAssembler getAssembler(String className, Locale locale);
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentName.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentName.java?rev=744608&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentName.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentName.java Sun Feb 15 04:44:12 2009
@@ -0,0 +1,50 @@
+// Copyright 2009 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.tapestry5.internal.pageload;
+
+/**
+ * Used to construct component names.
+ */
+public class ComponentName
+{
+    private final String pageName;
+
+    final String nestedId;
+
+    final String completeId;
+
+    ComponentName(String pageName)
+    {
+        this.pageName = pageName;
+        this.nestedId = null;
+        this.completeId = pageName;
+    }
+
+    private ComponentName(String pageName, String nestedId, String completeId)
+    {
+        this.completeId = completeId;
+        this.nestedId = nestedId;
+        this.pageName = pageName;
+    }
+
+    ComponentName child(String embeddedId)
+    {
+        String newNestedId = nestedId == null
+                             ? embeddedId
+                             : nestedId + "." + embeddedId;
+
+        return new ComponentName(pageName, newNestedId, pageName + ":" + newNestedId);
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssembler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssembler.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssembler.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssembler.java Sun Feb 15 04:44:12 2009
@@ -14,16 +14,14 @@
 
 package org.apache.tapestry5.internal.pageload;
 
-import org.apache.tapestry5.Binding;
-import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.internal.structure.ComponentPageElement;
+import org.apache.tapestry5.ioc.Locatable;
 
 /**
  * Encapsulates logic related to assembling an embedded component within a {@link org.apache.tapestry5.internal.pageload.ComponentAssembler}.
  */
-interface EmbeddedComponentAssembler
+interface EmbeddedComponentAssembler extends Locatable
 {
-    void addInstanceMixin(ComponentModel mixinModel);
-
     /**
      * Creates a binder that can later be used to bind the parameter. The parameter name may be unqualified ("value") or
      * have a mixin prefix ("mymixin.value").  In the former case, the correct mixin is located (though the more typical
@@ -32,23 +30,14 @@
      * <p/>
      * If the name of the parameter does not match a formal parameter of the component (or mixin) and the component (or
      * mixin) does not support informal parameters, then null is returned.
-     *
-     * @param parameterName        simple or qualified parameter name
-     * @param parameterValue       value of parameter (possibly having a binding prefix)
-     * @param defaultBindingPrefix default binding prefix to use if the parameter is informal
-     * @return object that can bind the parameter once the container and component have been instantiated, or null
-     */
-    ParameterBinder createBinder(String parameterName, String parameterValue, String defaultBindingPrefix);
-
-    /**
-     * Creates a ParameterBinding where the binding is already instantiated. Follows the same logic as {@link
-     * #createBinder(String, String, String)} in terms of finding the correct mixin and parameter name.
+     * <p/>
+     * This method should only be called at page-assembly time as it requires some data that is collected during
+     * ComponentAssembly construction in order to handle published parameters of embedded components.
      *
      * @param parameterName simple or qualified parameter name
-     * @param binding       binding for parameter
-     * @return object that can perform the binding, or null
+     * @return object that can bind the parameter
      */
-    ParameterBinder createBinder(String parameterName, Binding binding);
+    ParameterBinder createParameterBinder(String parameterName);
 
     /**
      * Checks to see if the parameter name  has been bound.
@@ -60,4 +49,12 @@
      * {@link org.apache.tapestry5.annotations.Component} annotation (even inherited bindings).
      */
     void setBound(String parameterName);
+
+
+    /**
+     * Adds mixins to the newly created embedded element.
+     *
+     * @param newElement new element requiring mixins
+     */
+    void addMixinsToElement(ComponentPageElement newElement);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java Sun Feb 15 04:44:12 2009
@@ -14,47 +14,68 @@
 
 package org.apache.tapestry5.internal.pageload;
 
-import org.apache.tapestry5.Binding;
-import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
 import org.apache.tapestry5.internal.services.ComponentInstantiatorSource;
-import org.apache.tapestry5.internal.services.PageElementFactory;
+import org.apache.tapestry5.internal.services.Instantiator;
 import org.apache.tapestry5.internal.structure.ComponentPageElement;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.model.ComponentModel;
-import org.apache.tapestry5.model.ParameterModel;
+import org.apache.tapestry5.model.EmbeddedComponentModel;
+import org.apache.tapestry5.services.ComponentClassResolver;
 
+import java.util.Locale;
 import java.util.Map;
 
 public class EmbeddedComponentAssemblerImpl implements EmbeddedComponentAssembler
 {
-    private final Map<String, ComponentModel> mixinIdToComponentModel = CollectionFactory.newCaseInsensitiveMap();
-
     private final ComponentInstantiatorSource instantiatorSource;
 
-    private final ComponentModel componentModel;
+    private final ComponentAssemblerSource assemblerSource;
 
-    private final PageElementFactory elementFactory;
+    private final Locale locale;
+
+    private final ComponentModel componentModel;
 
     private final Location location;
 
-    private Map<String, Boolean> bound;
+    private final Map<String, ComponentModel> mixinIdToComponentModel = CollectionFactory.newCaseInsensitiveMap();
 
-    interface BindingCreator
-    {
-        Binding newBinding(String parameterName, ComponentResources loadingComponentResources,
-                           ComponentResources embeddedComponentResources, String defaultBindingPrefix);
-    }
+    /**
+     * Maps parameter names (both simple, and qualified with the mixin id) to the corresponding QualifiedParameterName.
+     */
+    private final Map<String, ParameterBinder> parameterNameToBinder = CollectionFactory.newCaseInsensitiveMap();
+
+    // The id of the mixin to receive informal parameters.  If null, the component itself recieves them.
+    // If the component doesn't support them, they are quietly dropped.
 
-    public EmbeddedComponentAssemblerImpl(ComponentInstantiatorSource instantiatorSource,
-                                          PageElementFactory elementFactory,
+    private final String informalParametersMixinId;
+
+    private Map<String, Boolean> bound;
+
+    /**
+     * @param assemblerSource
+     * @param instantiatorSource     used to access component models
+     * @param componentClassResolver used to convert mixin types to component models
+     * @param componentClassName     class name of embedded component
+     * @param locale
+     * @param embeddedModel          embedded model (may be null for components defined in the template)
+     * @param templateMixins         list of mixins from the t:mixins element (possibly null)
+     * @param location               location of components element in its container's template
+     */
+    public EmbeddedComponentAssemblerImpl(ComponentAssemblerSource assemblerSource,
+                                          ComponentInstantiatorSource instantiatorSource,
+                                          ComponentClassResolver componentClassResolver,
                                           String componentClassName,
+                                          Locale locale, EmbeddedComponentModel embeddedModel,
+                                          String templateMixins,
                                           Location location)
     {
+        this.assemblerSource = assemblerSource;
         this.instantiatorSource = instantiatorSource;
-        this.elementFactory = elementFactory;
+        this.locale = locale;
         this.location = location;
 
         componentModel = getModel(componentClassName);
@@ -63,162 +84,136 @@
 
         for (String className : componentModel.getMixinClassNames())
         {
-            addInstanceMixin(getModel(className));
+            addMixin(className);
         }
 
-        // Instance mixins will be added later.
-    }
-
-    private ComponentModel getModel(String className)
-    {
-        return instantiatorSource.getInstantiator(className).getModel();
-    }
+        // If there's an embedded model (i.e., there was an @Component annotation)
+        // then it may define some mixins.
 
-    public void addInstanceMixin(ComponentModel mixinModel)
-    {
-        String mixinId = InternalUtils.lastTerm(mixinModel.getComponentClassName());
-
-        // TODO: Check for conflicts here?
-
-        mixinIdToComponentModel.put(mixinId, mixinModel);
-    }
-
-    public ParameterBinder createBinder(String parameterName, final String parameterValue, String defaultBindingPrefix)
-    {
-        BindingCreator creator = new BindingCreator()
+        if (embeddedModel != null)
         {
-            public Binding newBinding(String parameterName, ComponentResources loadingComponentResources,
-                                      ComponentResources embeddedComponentResources, String defaultBindingPrefix)
+            for (String className : embeddedModel.getMixinClassNames())
             {
-                return elementFactory.newBinding(parameterName, loadingComponentResources, embeddedComponentResources,
-                                                 defaultBindingPrefix, parameterValue, location);
+                addMixin(className);
             }
-        };
-
+        }
 
-        return createBinder(parameterName, defaultBindingPrefix, creator);
-    }
+        // And the template may include a t:mixins element to define yet more mixin.
 
-    public ParameterBinder createBinder(String parameterName, final Binding binding)
-    {
-        BindingCreator creator = new BindingCreator()
+        for (String mixinType : TapestryInternalUtils.splitAtCommas(templateMixins))
         {
-            public Binding newBinding(String parameterName, ComponentResources loadingComponentResources,
-                                      ComponentResources embeddedComponentResources, String defaultBindingPrefix)
-            {
-                return binding;
-            }
-        };
+            String className = componentClassResolver.resolveMixinTypeToClassName(mixinType);
 
-        return createBinder(parameterName, null, creator);
-    }
+            addMixin(className);
+        }
 
+        informalParametersMixinId = prescanMixins();
 
-    private ParameterBinder createBinder(String parameterName, String defaultBindingPrefix,
-                                         BindingCreator creator)
+    }
+
+    private String prescanMixins()
     {
-        int dotx = parameterName.indexOf('.');
+        String supportsInformals = null;
 
-        if (dotx > 0)
-            return createQualifiedParameterBinder(parameterName.substring(0, dotx),
-                                                  parameterName.substring(dotx + 1),
-                                                  defaultBindingPrefix,
-                                                  creator);
+        for (Map.Entry<String, ComponentModel> entry : mixinIdToComponentModel.entrySet())
+        {
+            String mixinId = entry.getKey();
+            ComponentModel mixinModel = entry.getValue();
 
-        // OK, see if its a parameter of the component (that takes precedence).
+            updateParameterNameToQualified(mixinId, mixinModel);
 
-        ParameterModel pmodel = componentModel.getParameterModel(parameterName);
+            if (supportsInformals == null && mixinModel.getSupportsInformalParameters())
+                supportsInformals = mixinId;
+        }
+
+        // The component comes last and overwrites simple names from the others.
 
-        if (pmodel != null)
-            return createBinder(null, parameterName, pmodel.getDefaultBindingPrefix(), creator);
+        updateParameterNameToQualified(null, componentModel);
 
-        String informalMixinId = null;
+        return supportsInformals;
+    }
 
-        for (Map.Entry<String, ComponentModel> me : mixinIdToComponentModel.entrySet())
+    private void updateParameterNameToQualified(String mixinId, ComponentModel model)
+    {
+        for (String parameterName : model.getParameterNames())
         {
-            String mixinId = me.getKey();
-            ComponentModel model = me.getValue();
+            String defaultBindingPrefix = model.getParameterModel(parameterName).getDefaultBindingPrefix();
 
-            if (informalMixinId == null && model.getSupportsInformalParameters())
-                informalMixinId = mixinId;
+            ParameterBinderImpl binder = new ParameterBinderImpl(mixinId, parameterName, defaultBindingPrefix);
 
-            pmodel = model.getParameterModel(parameterName);
+            parameterNameToBinder.put(parameterName,
+                                      binder);
 
-            if (pmodel != null)
-                return createBinder(mixinId, parameterName, pmodel.getDefaultBindingPrefix(), creator);
+            if (mixinId != null)
+                parameterNameToBinder.put(mixinId + "." + parameterName, binder);
         }
+    }
 
-        // OK, it doesn't match any formal parameter of the component or any mixin.
+    private void addMixin(String className)
+    {
+        ComponentModel model = getModel(className);
 
-        // If neither the component nor any of its mixins supports informal parameters,
-        // then return null to ignore the parameter.
+        String mixinId = InternalUtils.lastTerm(className);
 
-        if (informalMixinId == null && !componentModel.getSupportsInformalParameters()) return null;
+        if (mixinIdToComponentModel.containsKey(mixinId))
+            throw new TapestryException(
+                    String.format("Mixins applied to a component must be unique. Mixin '%s' has already been applied.",
+                                  mixinId),
+                    location, null);
 
-        // Add as an informal parameter either to a mixin (if an mixin supprting informatl parameters
-        // was found) or to the component itself (otherwise).
 
-        return createBinder(informalMixinId, parameterName, defaultBindingPrefix, creator);
+        mixinIdToComponentModel.put(mixinId, model);
     }
 
-    private ParameterBinder createQualifiedParameterBinder(String mixinId, String parameterName,
-                                                           String defaultBindingPrefix,
-                                                           BindingCreator creator)
+    private ComponentModel getModel(String className)
     {
-        ComponentModel mixinModel = mixinIdToComponentModel.get(mixinId);
+        return instantiatorSource.getInstantiator(className).getModel();
+    }
 
-        if (mixinModel == null)
+    public ParameterBinder createParameterBinder(String parameterName)
+    {
+        int dotx = parameterName.indexOf('.');
+        if (dotx >= 0)
         {
-            String message = String.format(
-                    "Parameter '%s.%s' does not match any defined mixins for this component.  Available mixins: %s.",
-                    mixinId, parameterName,
-                    InternalUtils.sortedKeys(mixinIdToComponentModel));
+            String mixinId = parameterName.substring(0, dotx);
+            if (!mixinIdToComponentModel.containsKey(mixinId))
+            {
+                String message = String.format("Mixin id for parameter '%s' not found. Attached mixins: %s.",
+                                               parameterName,
+                                               InternalUtils.joinSorted(mixinIdToComponentModel.keySet()));
 
-            throw new TapestryException(message, location, null);
+                throw new TapestryException(message, location, null);
+            }
         }
+        else
+        {
+            // Unqualified parameter name. May be a reference not to a parameter of this component, but a published
+            // parameter of a component embedded in this component. The ComponentAssembler for this component
+            // will know.
 
-        return createBinder(mixinId, mixinModel, parameterName, defaultBindingPrefix, creator);
-    }
+            ComponentAssembler assembler = assemblerSource.getAssembler(componentModel.getComponentClassName(), locale);
 
-    private ParameterBinder createBinder(String mixinId,
-                                         ComponentModel model,
-                                         String parameterName,
-                                         String defaultBindingPrefix,
-                                         BindingCreator creator)
-    {
-        ParameterModel pmodel = model.getParameterModel(parameterName);
+            ParameterBinder binder = assembler.getBinder(parameterName);
 
-        // Ignore informal parameters for mixins that don't support them.
+            if (binder != null) return binder;
+        }
 
-        if (pmodel == null && !model.getSupportsInformalParameters())
-            return null;
+        final ParameterBinder binder = parameterNameToBinder.get(parameterName);
 
-        final String bindingPrefix = pmodel == null ? defaultBindingPrefix : pmodel.getDefaultBindingPrefix();
+        if (binder != null)
+            return binder;
 
-        return createBinder(mixinId, parameterName, bindingPrefix, creator);
-    }
+        // Informal parameter: Is there a mixin for that?
 
-    private ParameterBinder createBinder(final String mixinId,
-                                         final String parameterName,
-                                         final String defaultBindingPrefix,
-                                         final BindingCreator creator)
-    {
-        return new ParameterBinder()
-        {
-            public void bind(ComponentPageElement container, ComponentPageElement embedded)
-            {
-                Binding binding =
-                        creator.newBinding(parameterName,
-                                           container.getComponentResources(),
-                                           embedded.getComponentResources(),
-                                           defaultBindingPrefix);
-
-                if (mixinId == null)
-                    embedded.bindParameter(parameterName, binding);
-                else
-                    embedded.bindMixinParameter(mixinId, parameterName, binding);
-            }
-        };
+        if (informalParametersMixinId != null)
+            return new ParameterBinderImpl(informalParametersMixinId, parameterName, null);
+
+        if (componentModel.getSupportsInformalParameters())
+            return new ParameterBinderImpl(null, parameterName, null);
+
+        // Otherwise, informal parameter and not supported by the component or any mixin.
+
+        return null;
     }
 
     public boolean isBound(String parameterName)
@@ -233,4 +228,25 @@
 
         bound.put(parameterName, true);
     }
+
+    public void addMixinsToElement(ComponentPageElement newElement)
+    {
+        for (Map.Entry<String, ComponentModel> entry : mixinIdToComponentModel.entrySet())
+        {
+            String mixinId = entry.getKey();
+            ComponentModel model = entry.getValue();
+
+            // TODO: Change mixinIdTo... to be to Instantiator instead, so we don't have to
+            // keep asking the IS for them.
+
+            Instantiator instantiator = instantiatorSource.getInstantiator(model.getComponentClassName());
+
+            newElement.addMixin(mixinId, instantiator);
+        }
+    }
+
+    public Location getLocation()
+    {
+        return location;
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java Sun Feb 15 04:44:12 2009
@@ -17,7 +17,9 @@
 import org.apache.tapestry5.internal.structure.BodyPageElement;
 import org.apache.tapestry5.internal.structure.ComponentPageElement;
 import org.apache.tapestry5.internal.structure.Page;
+import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.ioc.util.Stack;
 import org.apache.tapestry5.runtime.RenderCommand;
 
@@ -30,17 +32,19 @@
  */
 class PageAssembly
 {
-    public final Page page;
+    final Page page;
 
-    public final Stack<ComponentPageElement> activeElement = CollectionFactory.newStack();
+    final Stack<ComponentPageElement> activeElement = CollectionFactory.newStack();
 
-    public final Stack<BodyPageElement> bodyElement = CollectionFactory.newStack();
+    final Stack<BodyPageElement> bodyElement = CollectionFactory.newStack();
 
-    public final Stack<ComponentPageElement> createdElement = CollectionFactory.newStack();
+    final Stack<ComponentPageElement> createdElement = CollectionFactory.newStack();
 
-    public final Stack<EmbeddedComponentAssembler> embeddedAssembler = CollectionFactory.newStack();
+    final Stack<ComponentName> componentName = CollectionFactory.newStack();
 
-    public final List<PageAssemblyAction> deferred = CollectionFactory.newList();
+    final Stack<EmbeddedComponentAssembler> embeddedAssembler = CollectionFactory.newStack();
+
+    final List<PageAssemblyAction> deferred = CollectionFactory.newList();
 
     private final List<RenderCommand> composableRenderCommands = CollectionFactory.newList();
 
@@ -57,7 +61,7 @@
      *
      * @param command
      */
-    public void addRenderCommand(RenderCommand command)
+    void addRenderCommand(RenderCommand command)
     {
         flushComposableRenderCommands();
 
@@ -70,7 +74,7 @@
      *
      * @param command
      */
-    public void addComposableRenderCommand(RenderCommand command)
+    void addComposableRenderCommand(RenderCommand command)
     {
         composableRenderCommands.add(command);
     }
@@ -79,7 +83,7 @@
      * Adds any composed render commands to the top element of the bodyElement stack. Render commands may be combined as
      * a {@link org.apache.tapestry5.internal.pageload.CompositeRenderCommand}.
      */
-    public void flushComposableRenderCommands()
+    void flushComposableRenderCommands()
     {
         int count = composableRenderCommands.size();
 
@@ -102,7 +106,7 @@
         composableRenderCommands.clear();
     }
 
-    public boolean checkAndSetFlag(String flagName)
+    boolean checkAndSetFlag(String flagName)
     {
         boolean result = flags.contains(flagName);
 
@@ -111,6 +115,23 @@
 
         return result;
     }
+
+    void checkForRecursion(String componentClassName, Location location)
+    {
+        for (Object o : activeElement.getSnapshot())
+        {
+            ComponentPageElement e = (ComponentPageElement) o;
+
+            if (e.getComponentResources().getComponentModel().getComponentClassName().equals(componentClassName))
+            {
+                String message = String.format(
+                        "The template for component %s is recursive (contains another direct or indirect reference to component %<s). This is not supported (components may not contain themselves).",
+                        componentClassName);
+
+                throw new TapestryException(message, location, null);
+            }
+        }
+    }
 }
 
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java?rev=744608&r1=744607&r2=744608&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java Sun Feb 15 04:44:12 2009
@@ -20,7 +20,6 @@
 import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.internal.InternalConstants;
-import org.apache.tapestry5.internal.TapestryInternalUtils;
 import org.apache.tapestry5.internal.bindings.LiteralBinding;
 import org.apache.tapestry5.internal.parser.*;
 import org.apache.tapestry5.internal.services.*;
@@ -36,15 +35,25 @@
 import org.apache.tapestry5.services.ComponentClassResolver;
 import org.apache.tapestry5.services.InvalidationListener;
 
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
 /**
  * There's still a lot of room to beef up {@link org.apache.tapestry5.internal.pageload.ComponentAssembler} and {@link
  * org.apache.tapestry5.internal.pageload.EmbeddedComponentAssembler} to perform more static analysis.
+ * <p/>
+ * Loading a page involves a recurive process of creating {@link org.apache.tapestry5.internal.pageload.ComponentAssembler}s:
+ * for the root component, then down the tree for each embedded component. A ComponentAssembler is largely a collection
+ * of {@link org.apache.tapestry5.internal.pageload.PageAssemblyAction}s. Once created, a ComponentAssembler can quickly
+ * assemble any number of component instances. All of the expensive logic, such as fitting template tokens together and
+ * matching parameters to bindings, is done as part of the one-time construction of the ComponentAssembler. The end
+ * result removes a huge amount of computational redundancy that was present in Tapestry 5.0, but to understand this,
+ * you need to split your mind into two phases: construction (of the ComponentAssemblers) and assembly. It's twisted ...
+ * and perhaps a bit functional and Monadic.
+ * <p/>
+ * And truly, <em>This is the Tapestry Heart, This is the Tapestry Soul...</em>
  */
-public class PageLoaderImpl implements PageLoader, InvalidationListener
+public class PageLoaderImpl implements PageLoader, InvalidationListener, ComponentAssemblerSource
 {
     private static final class Key
     {
@@ -181,7 +190,7 @@
         return page;
     }
 
-    private ComponentAssembler getAssembler(String className, Locale locale)
+    public ComponentAssembler getAssembler(String className, Locale locale)
     {
         Key key = new Key(className, locale);
 
@@ -206,7 +215,8 @@
 
         ComponentPageElementResources resources = resourcesSource.get(locale);
 
-        ComponentAssembler assembler = new ComponentAssemblerImpl(instantiator, resources);
+        ComponentAssembler assembler = new ComponentAssemblerImpl(this, instantiatorSource, componentClassResolver,
+                                                                  instantiator, resources, locale);
 
         // "Program" the assembler by adding actions to it. The actions execute every time a new page instance
         // is needed.
@@ -224,40 +234,9 @@
     {
         assembler.validateEmbeddedIds(template);
 
-        // TODO: Optimize for the case of the empty template.
-
-        addImplementationMixins(assembler);
-
         processTemplate(assembler, template);
     }
 
-    /**
-     * Add any mixins defined for the component (implementation mixins, rather than instance mixins).
-     */
-
-    private void addImplementationMixins(ComponentAssembler assembler)
-    {
-        final List<String> mixinClassNames = assembler.getModel().getMixinClassNames();
-
-        if (mixinClassNames.isEmpty()) return;
-
-        assembler.add(new PageAssemblyAction()
-        {
-            public void execute(PageAssembly pageAssembly)
-            {
-                ComponentPageElement element = pageAssembly.activeElement.peek();
-
-                for (String className : mixinClassNames)
-                {
-                    Instantiator mixinInstantiator = instantiatorSource.getInstantiator(className);
-
-                    element.addMixin(InternalUtils.lastTerm(className), mixinInstantiator);
-
-                }
-            }
-        });
-    }
-
     private void processTemplate(ComponentAssembler assembler, ComponentTemplate template)
     {
         if (template.isMissing())
@@ -429,7 +408,7 @@
 
                 EmbeddedComponentAssembler embeddedAssembler = pageAssembly.embeddedAssembler.peek();
 
-                ParameterBinder binder = embeddedAssembler.createBinder(parameterName, binding);
+                ParameterBinder binder = embeddedAssembler.createParameterBinder(parameterName);
 
                 if (binder == null)
                 {
@@ -441,7 +420,7 @@
                     throw new TapestryException(message, token.getLocation(), null);
                 }
 
-                binder.bind(pageAssembly.activeElement.peek(), element);
+                binder.bind(pageAssembly.createdElement.peek(), binding);
 
                 pageAssembly.bodyElement.push(block);
             }
@@ -454,15 +433,12 @@
     {
         final BlockToken token = stream.next(BlockToken.class);
 
-        final String blockId = token.getId();
-
-        // TODO: The description string should be calculatable statically, we just need
-        // to keep yet another stack (of complete component ids).
-
         assembler.add(new PageAssemblyAction()
         {
             public void execute(PageAssembly pageAssembly)
             {
+                String blockId = token.getId();
+
                 ComponentPageElement element = pageAssembly.activeElement.peek();
 
                 String description = blockId == null
@@ -552,7 +528,7 @@
                                           AttributeToken token)
     {
         addParameterBindingAction(assembler, embeddedAssembler, token.getName(), token.getValue(),
-                                  BindingConstants.LITERAL);
+                                  BindingConstants.LITERAL, token.getLocation());
     }
 
     private void element(ComponentAssembler assembler, TokenStream stream)
@@ -653,19 +629,16 @@
 
         // OK, now we can record an action to get it instantiated.
 
-        EmbeddedComponentAssembler embeddedAssembler = new EmbeddedComponentAssemblerImpl(instantiatorSource,
-                                                                                          elementFactory,
-                                                                                          componentClassName,
-                                                                                          token.getLocation());
-
-        addActionForEmbeddedComponent(assembler, embeddedAssembler, embeddedId, elementName, componentClassName,
-                                      token.getLocation());
+        EmbeddedComponentAssembler embeddedAssembler =
+                assembler.createEmbeddedAssembler(embeddedId,
+                                                  componentClassName,
+                                                  embeddedModel,
+                                                  token.getMixins(),
+                                                  token.getLocation());
 
+        addActionForEmbeddedComponent(assembler, embeddedAssembler, embeddedId, elementName, componentClassName);
 
-        addMixinActions(assembler, embeddedAssembler, embeddedModel, token.getMixins());
-
-        bindParametersFromEmbeddedModel(assembler, embeddedAssembler, embeddedModel);
-
+        addParameterBindingActions(assembler, embeddedAssembler, embeddedModel);
 
         if (embeddedModel != null && embeddedModel.getInheritInformalParameters())
         {
@@ -715,25 +688,31 @@
         }
     }
 
-    private void bindParametersFromEmbeddedModel(ComponentAssembler assembler,
-                                                 EmbeddedComponentAssembler embeddedAssembler,
-                                                 EmbeddedComponentModel embeddedModel)
+    private void addParameterBindingActions(ComponentAssembler assembler,
+                                            EmbeddedComponentAssembler embeddedAssembler,
+                                            EmbeddedComponentModel embeddedModel)
     {
         if (embeddedModel == null) return;
 
-        for (String name : embeddedModel.getParameterNames())
+        for (String parameterName : embeddedModel.getParameterNames())
         {
-            String parameterValue = embeddedModel.getParameterValue(name);
+            String parameterValue = embeddedModel.getParameterValue(parameterName);
 
-            addParameterBindingAction(assembler, embeddedAssembler, name, parameterValue, BindingConstants.PROP);
+            addParameterBindingAction(assembler,
+                                      embeddedAssembler,
+                                      parameterName,
+                                      parameterValue,
+                                      BindingConstants.PROP,
+                                      embeddedModel.getLocation());
         }
     }
 
     private void addParameterBindingAction(ComponentAssembler assembler,
-                                           EmbeddedComponentAssembler embeddedAssembler,
-                                           String parameterName,
-                                           String parameterValue,
-                                           String defaultBindingPrefix)
+                                           final EmbeddedComponentAssembler embeddedAssembler,
+                                           final String parameterName,
+                                           final String parameterValue,
+                                           final String metaDefaultBindingPrefix,
+                                           final Location location)
     {
         if (embeddedAssembler.isBound(parameterName)) return;
 
@@ -741,29 +720,45 @@
 
         if (parameterValue.startsWith(InternalConstants.INHERIT_BINDING_PREFIX))
         {
-
             String containerParameterName = parameterValue.substring(InternalConstants.INHERIT_BINDING_PREFIX.length());
 
-            addInhertedBindingAction(assembler, parameterName, containerParameterName);
+            addInheritedBindingAction(assembler, parameterName, containerParameterName);
             return;
         }
 
 
-        final ParameterBinder binder = embeddedAssembler.createBinder(parameterName, parameterValue,
-                                                                      defaultBindingPrefix);
-
-        // Null meaning an informal parameter and the component (and mixins) doesn't support informals.
-
-        if (binder != null)
+        assembler.add(new PageAssemblyAction()
         {
-            assembler.add(new PageAssemblyAction()
+            public void execute(PageAssembly pageAssembly)
             {
-                public void execute(PageAssembly pageAssembly)
+                // Because of published parameters, we have to wait until page assembly time to throw out
+                // informal parameters bound to components that don't support informal parameters ...
+                // otherwise we'd throw out (sometimes!) published parameters.
+
+                final ParameterBinder binder = embeddedAssembler.createParameterBinder(parameterName);
+
+                // Null meaning an informal parameter and the component (and mixins) doesn't support informals.
+
+                if (binder != null)
                 {
-                    binder.bind(pageAssembly.activeElement.peek(), pageAssembly.createdElement.peek());
+                    final String defaultBindingPrefix = binder.getDefaultBindingPrefix(metaDefaultBindingPrefix);
+
+                    InternalComponentResources containerResources = pageAssembly.activeElement.peek().getComponentResources();
+
+                    ComponentPageElement embeddedElement = pageAssembly.createdElement.peek();
+                    InternalComponentResources embeddedResources = embeddedElement.getComponentResources();
+
+                    Binding binding = elementFactory.newBinding(parameterName,
+                                                                containerResources,
+                                                                embeddedResources,
+                                                                defaultBindingPrefix,
+                                                                parameterValue,
+                                                                location);
+
+                    binder.bind(embeddedElement, binding);
                 }
-            });
-        }
+            }
+        });
     }
 
     /**
@@ -774,9 +769,9 @@
      * @param parameterName
      * @param containerParameterName
      */
-    private void addInhertedBindingAction(ComponentAssembler assembler,
-                                          final String parameterName,
-                                          final String containerParameterName)
+    private void addInheritedBindingAction(ComponentAssembler assembler,
+                                           final String parameterName,
+                                           final String containerParameterName)
     {
         assembler.add(new PageAssemblyAction()
         {
@@ -805,13 +800,13 @@
                                            String containerParameterName)
     {
         // TODO: This assumes that the two parameters are both on the core component and not on
-        // a mixin.
+        // a mixin. I think this could be improved with more static analysis.
 
         Binding containerBinding = container.getBinding(containerParameterName);
 
         if (containerBinding == null) return;
 
-        String description = String.format("InheritedBinding[parameter %s %s(inherited from %s of %s)]",
+        String description = String.format("InheritedBinding[parameter %s %s (inherited from %s of %s)]",
                                            parameterName,
                                            embedded.getCompleteId(),
                                            containerParameterName,
@@ -821,84 +816,23 @@
         // to the location of the inherited binding, rather than the container component's
         // binding.
 
-        Binding inherited = new InheritedBinding(description, containerBinding, embedded.getLocation());
+        // Binding inherited = new InheritedBinding(description, containerBinding, embedded.getLocation());
 
-        embedded.bindParameter(parameterName, inherited);
-    }
-
-    private ComponentModel getModel(String componentClassName)
-    {
-        return instantiatorSource.getInstantiator(componentClassName).getModel();
-    }
-
-    private void addMixinActions(ComponentAssembler assembler,
-                                 EmbeddedComponentAssembler embeddedAssembler, EmbeddedComponentModel model,
-                                 String mixins
-    )
-    {
-        if (model != null)
-        {
-            addMixinActionsFromEmbeddedModel(assembler, embeddedAssembler, model);
-        }
-
-        List<String> classNames = CollectionFactory.newList();
-
-        for (String typeName : TapestryInternalUtils.splitAtCommas(mixins))
-        {
-            classNames.add(componentClassResolver.resolveMixinTypeToClassName(typeName));
-        }
-
-        addMixinActionsForClassNames(assembler, embeddedAssembler, classNames);
-    }
-
-    private void addMixinActionsFromEmbeddedModel(ComponentAssembler assembler,
-                                                  EmbeddedComponentAssembler embeddedAssembler,
-                                                  EmbeddedComponentModel model
-    )
-    {
-        addMixinActionsForClassNames(assembler, embeddedAssembler, model.getMixinClassNames());
-    }
-
-    private void addMixinActionsForClassNames(ComponentAssembler assembler,
-                                              EmbeddedComponentAssembler embeddedAssembler,
-                                              final List<String> classNames
-    )
-    {
-        for (String className : classNames)
-        {
-            embeddedAssembler.addInstanceMixin(getModel(className));
-        }
-
-        if (!classNames.isEmpty())
-        {
-            assembler.add(new PageAssemblyAction()
-            {
-                public void execute(PageAssembly pageAssembly)
-                {
-                    ComponentPageElement embedded = pageAssembly.createdElement.peek();
-
-                    for (String className : classNames)
-                    {
-                        Instantiator instantiator = instantiatorSource.getInstantiator(className);
-
-                        embedded.addMixin(InternalUtils.lastTerm(className), instantiator);
-                    }
-                }
-            });
-        }
+        embedded.bindParameter(parameterName, containerBinding);
     }
 
     private void addActionForEmbeddedComponent(ComponentAssembler assembler,
                                                final EmbeddedComponentAssembler embeddedAssembler,
                                                final String embeddedId,
                                                final String elementName,
-                                               final String componentClassName,
-                                               final Location location)
+                                               final String componentClassName)
     {
         assembler.add(new PageAssemblyAction()
         {
             public void execute(PageAssembly pageAssembly)
             {
+                pageAssembly.checkForRecursion(componentClassName, embeddedAssembler.getLocation());
+
                 pageAssembly.flushComposableRenderCommands();
 
                 Locale locale = pageAssembly.page.getLocale();
@@ -907,7 +841,8 @@
 
                 // Remeber: this pushes onto to the createdElement stack, but does not pop it.
 
-                assemblerForSubcomponent.assembleEmbeddedComponent(pageAssembly, embeddedId, elementName, location);
+                assemblerForSubcomponent.assembleEmbeddedComponent(pageAssembly, embeddedAssembler, embeddedId,
+                                                                   elementName, embeddedAssembler.getLocation());
 
                 // ... which is why we can find it via peek() here.  And it's our responsibility
                 // to clean it up.

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinder.java?rev=744608&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinder.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ParameterBinder.java Sun Feb 15 04:44:12 2009
@@ -0,0 +1,47 @@
+// Copyright 2009 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.tapestry5.internal.pageload;
+
+import org.apache.tapestry5.Binding;
+import org.apache.tapestry5.internal.structure.ComponentPageElement;
+
+/**
+ * Encapsulates the result of a parameter lookup, i.e., the conversion from qualified or unqualified parameter name to
+ * mixin id (or null) and parameter name, as well as the default binding prefix for the parameter.
+ *
+ * @see org.apache.tapestry5.internal.pageload.EmbeddedComponentAssembler#createParameterBinder(String)
+ */
+interface ParameterBinder
+{
+    /**
+     * Bindings the parameter of the element.   The name (and optionally mixinid) of the parameter is determined when
+     * the ParameterBinder is created.
+     *
+     * @param element page element to bind
+     * @param binding binding for the parameter
+     */
+    void bind(ComponentPageElement element, Binding binding);
+
+    /**
+     * Returns the correct default binding prefix to use for this parameter, which is either the {@linkplain
+     * org.apache.tapestry5.model.ParameterModel#getDefaultBindingPrefix() default binding prefix configured for the
+     * parameter}, or the meta-default (when binding an informal parameter). A specific binding of a parameter may
+     * always override the default binding prefix, however it is calculated.
+     *
+     * @param metaDefault
+     * @return binding prefix
+     */
+    String getDefaultBindingPrefix(String metaDefault);
+}