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 2010/01/22 17:31:49 UTC

svn commit: r902157 [1/2] - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/ main/java/org/apache/tapestry5/annotations/ main/java/org/apache/tapestry5/internal/ main/java/org/apache/tapestry5/internal/model/ main/java/or...

Author: hlship
Date: Fri Jan 22 16:31:47 2010
New Revision: 902157

URL: http://svn.apache.org/viewvc?rev=902157&view=rev
Log:
Rewrite BindParameterWorker using the new ClassTransformation APIs

Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.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/ComponentPageElementImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.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/internal/transform/BindParameterWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/TransformMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.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/structure/StructureStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/transform/TransformStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/TextOnlyOnDisabled.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java Fri Jan 22 16:31:47 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -76,9 +76,11 @@
 
     /**
      * Returns an embedded component, given the component's id.
-     *
-     * @param embeddedId selects the embedded component (case is ignored)
-     * @throws IllegalArgumentException if this component does not contain a component with the given id
+     * 
+     * @param embeddedId
+     *            selects the embedded component (case is ignored)
+     * @throws IllegalArgumentException
+     *             if this component does not contain a component with the given id
      */
 
     Component getEmbeddedComponent(String embeddedId);
@@ -90,9 +92,11 @@
 
     /**
      * Obtains an annotation provided by a parameter.
-     *
-     * @param parameterName  name of parameter to search for the annotation
-     * @param annotationType the type of annotation
+     * 
+     * @param parameterName
+     *            name of parameter to search for the annotation
+     * @param annotationType
+     *            the type of annotation
      * @return the annotation if found or null otherwise
      */
     <T extends Annotation> T getParameterAnnotation(String parameterName, Class<T> annotationType);
@@ -100,8 +104,9 @@
     /**
      * Indentifies all parameters that are not formal parameters and writes each as a attribute/value pair into the
      * current element of the markup writer.
-     *
-     * @param writer to which {@link MarkupWriter#attributes(Object[]) attributes} will be written
+     * 
+     * @param writer
+     *            to which {@link MarkupWriter#attributes(Object[]) attributes} will be written
      */
     void renderInformalParameters(MarkupWriter writer);
 
@@ -115,8 +120,9 @@
      * with property bindings, and is used to determine the actual type of the property, rather than the type of
      * parameter (remember that type coercion automatically occurs, which can mask significant differences between the
      * parameter type and the bound property type).
-     *
-     * @param parameterName used to select the parameter (case is ignored)
+     * 
+     * @param parameterName
+     *            used to select the parameter (case is ignored)
      * @return the type of the bound parameter, or null if the parameter is not bound
      * @see Binding#getBindingType()
      */
@@ -124,35 +130,42 @@
 
     /**
      * Returns an annotation provider, used to obtain annotations related to the parameter.
-     *
-     * @param parameterName used to select the parameter (case is ignored)
+     * 
+     * @param parameterName
+     *            used to select the parameter (case is ignored)
      * @return the annotation provider, or null if the parameter is not bound
      */
     AnnotationProvider getAnnotationProvider(String parameterName);
 
     /**
      * Used to access an informal parameter that's a Block.
-     *
-     * @param parameterName the name of the informal parameter (case is ignored)
+     * 
+     * @param parameterName
+     *            the name of the informal parameter (case is ignored)
      * @return the informal Block parameter, or null if not bound
      */
     Block getBlockParameter(String parameterName);
 
     /**
      * Returns a previously stored render variable.
-     *
-     * @param name of the variable (case will be ignored)
+     * 
+     * @param name
+     *            of the variable (case will be ignored)
      * @return the variable's value
-     * @throws IllegalArgumentException if the name doesn't correspond to a stored value
+     * @throws IllegalArgumentException
+     *             if the name doesn't correspond to a stored value
      */
     Object getRenderVariable(String name);
 
     /**
      * Stores a render variable, accessible with the provided name.
-     *
-     * @param name  of value to store
-     * @param value value to store (may not be null)
-     * @throws IllegalStateException if the component is not currently rendering
+     * 
+     * @param name
+     *            of value to store
+     * @param value
+     *            value to store (may not be null)
+     * @throws IllegalStateException
+     *             if the component is not currently rendering
      */
     void storeRenderVariable(String name, Object value);
 
@@ -161,9 +174,8 @@
      */
     void addPageLifecycleListener(PageLifecycleListener listener);
 
-
     /**
-     * Discards all persistent field changes for the page containing the component.  Changes are eliminated from
+     * Discards all persistent field changes for the page containing the component. Changes are eliminated from
      * persistent storage (such as the {@link org.apache.tapestry5.services.Session}) which will take effect in the
      * <em>next</em> request (the attached page instance is not affected).
      */
@@ -171,20 +183,20 @@
 
     /**
      * Returns the name of element that represents the component in its template, or null if not known.
-     *
+     * 
      * @return the element name or null
      */
     String getElementName();
 
     /**
      * Returns a list of the names of any informal parameters bound to this component.
-     *
+     * 
      * @return the name sorted alphabetically
      * @see org.apache.tapestry5.annotations.SupportsInformalParameters
      */
     List<String> getInformalParameterNames();
 
-    /**
+/**
      * Reads an informal parameter and {@linkplain org.apache.tapestry5.ioc.services.TypeCoercer coercers) the bound
      * value to the indicated type.
      *
@@ -193,4 +205,12 @@
      * @return instance of type
      */
     <T> T getInformalParameter(String name, Class<T> type);
+
+    /**
+     * Returns true if these resources represent a mixin to another component. The component is the
+     * {@linkplain #getContainerResources() container} of this resources.
+     * 
+     * @since 5.2.0
+     */
+    boolean isMixin();
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java Fri Jan 22 16:31:47 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -24,18 +24,16 @@
 import org.apache.tapestry5.internal.transform.BindParameterWorker;
 import org.apache.tapestry5.ioc.annotations.UseWith;
 
-
 /**
  * Designates a field in a mixin which is bound to the parameter of the containing
  * component corresponding to the value of the annotation. If no value is specified,
  * the bound parameter name is assumed to match the field name of the mixin.
- *
  * For example, a mixin intended to work with form fields would define a field named
- * "value", marked by this annotation.  The user-variable bound to the component's value
+ * "value", marked by this annotation. The user-variable bound to the component's value
  * parameter would ultimately be bound in a chain:
  * uservariable <=> mixin.value <=> component.value.
  * Changes to any one value in the chain will be propagated accordingly.
- *
+ * 
  * @since 5.2.0
  * @see BindParameterWorker
  */
@@ -49,9 +47,13 @@
      * @return the name of the mixin bound-parameter, exactly as for the Parameter annotation.
      */
     String name() default "";
+
     /**
-     * @return the name(s) of the parent parameter to bind. Defaults to the name of the mixin field. If more than one
-     *         name is specified, the first name matching a declared parameter of the core component will be used.
+     * @return the name(s) of the parent parameter to bind. Defaults to the name of the mixin field.
+     *         If more than one
+     *         name is specified, the first name matching a declared parameter of the core component
+     *         will be used.
      */
-    String[] value() default {""};
+    String[] value() default
+    { "" };
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java Fri Jan 22 16:31:47 2010
@@ -20,7 +20,6 @@
 import org.apache.tapestry5.internal.structure.PageResetListener;
 import org.apache.tapestry5.internal.transform.ParameterConduit;
 import org.apache.tapestry5.runtime.RenderQueue;
-import org.apache.tapestry5.services.FieldValueConduit;
 
 /**
  * An extension of {@link org.apache.tapestry5.ComponentResources} that represents additional
@@ -72,22 +71,6 @@
     ParameterAccess getParameterAccess(String parameterName);
 
     /**
-     * Gets access object suitable for handling mixin fields which are bound to a parameter of the
-     * core component
-     * 
-     * @param boundParameterName
-     *            the name of the mixin field that should be linked to the core component's field.
-     * @param parentParameterNames
-     *            the list of parameter names to try in the parent. The first name that matches a
-     *            declared parameter name in the core component will be used. This allows
-     *            BindParameter to be used with
-     *            mixins that have a similar parameter type with different parameter names (eg:
-     * @since 5.2.0
-     */
-    ParameterAccess getContainerBoundParameterAccess(String boundParameterName,
-            String... parentParameterNames);
-
-    /**
      * Delegates to
      * {@link Page#addResetListener(org.apache.tapestry5.internal.structure.PageResetListener)}.
      * 
@@ -97,7 +80,7 @@
     void addPageResetListener(PageResetListener listener);
 
     /** @since 5.2.0 */
-    FieldValueConduit getParameterConduit(String parameterName);
+    ParameterConduit getParameterConduit(String parameterName);
 
     /** @since 5.2.0 */
     void setParameterConduit(String parameterName, ParameterConduit conduit);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java Fri Jan 22 16:31:47 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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
+// 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,
@@ -28,7 +28,7 @@
 {
     /**
      * Returns true if the component has finished loading. Initially, this value will be false.
-     *
+     * 
      * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
      */
     boolean isLoaded();
@@ -41,8 +41,9 @@
 
     /**
      * Returns the binding for the given parameter name, or null.
-     *
-     * @param parameterName name of component parameter
+     * 
+     * @param parameterName
+     *            name of component parameter
      * @return binding if bound, or null
      * @since 5.1.0.0
      */
@@ -50,31 +51,18 @@
 
     /**
      * Returns the mixin instance for the fully qualfied mixin class name.
-     *
-     * @param mixinClassName fully qualified class name
+     * 
+     * @param mixinClassName
+     *            fully qualified class name
      * @return IllegalArgumentException if no such mixin is associated with the core component
      */
     Component getMixinByClassName(String mixinClassName);
 
     /**
-     * Returns true if the mixin named by mixinClassName is associated with the core component
-     * @param mixinClassName fully qualified class name
-     * @since 5.2.0.0
-     */
-    boolean isMixingIn(String mixinClassName);
-
-    /**
-     * Registers an action that should be executed immediately after page load.
-     * @since 5.2.0.0
-     */
-    void deferLoadAction(Runnable action);
-
-    /**
      * Constructs a map linking informal parameters to the corresponding bindings.
-     *
+     * 
      * @return map, possible empty
      */
     Map<String, Binding> getInformalParameterBindings();
 
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java Fri Jan 22 16:31:47 2010
@@ -1,10 +1,10 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 2008, 2009, 2010 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
+// 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,
@@ -19,49 +19,40 @@
 /**
  * A wrapper around a named parameter provided by {@link org.apache.tapestry5.internal.InternalComponentResources}. The
  * parameter may be bound or unbound.
- *
+ * 
  * @since 5.0.19
  */
 public interface ParameterAccess extends AnnotationProvider
 {
     /**
      * Is the parameter bound?
-     *
+     * 
      * @return true if bound
      */
     boolean isBound();
 
     /**
-     * Reads the current value of the parameter via the parameter's {@link org.apache.tapestry5.Binding}. This method is
-     * intended for use from generated code (where it is easier to specify the type as a name than a Class instance).
-     *
-     * @param desiredTypeName the fully qualified name of the Java type to coerce the result to
-     * @return the value of the parameter, or null if not bound
-     * @throws NullPointerException if the parameter's value is null and null is not allowed
-     */
-    Object read(String desiredTypeName);
-
-
-    /**
      * Reads the value of a parameter, via the parameter's {@link org.apache.tapestry5.Binding}.
-     *
+     * 
      * @param <T>
-     * @param expectedType the expected type of parameter
+     * @param expectedType
+     *            the expected type of parameter
      * @return the value for the parameter, or null if the parameter is not bound.
      */
     <T> T read(Class<T> expectedType);
 
     /**
-     * Updates the parameter to a new value.  If the parameter is not bound, this method does nothing.
-     *
-     * @param parameterValue new value for parameter
+     * Updates the parameter to a new value. If the parameter is not bound, this method does nothing.
+     * 
+     * @param parameterValue
+     *            new value for parameter
      * @param <T>
      */
     <T> void write(T parameterValue);
 
     /**
      * Returns true if the binding is bound, and the binding is invariant.
-     *
+     * 
      * @return true if invariant
      * @see org.apache.tapestry5.Binding#isInvariant()
      */
@@ -72,30 +63,9 @@
      * with property bindings, and is used to determine the actual type of the property, rather than the type of
      * parameter (remember that type coercion automatically occurs, which can mask significant differences between the
      * parameter type and the bound property type).
-     *
+     * 
      * @return the type of the bound parameter, or null if the parameter is not bound
      * @see org.apache.tapestry5.Binding#getBindingType()
      */
     Class getBoundType();
-
-    /**
-     * Registers the given ParameterChangeListener. Registering a listener which is already 
-     * registered is silently ignored.
-     * @param listener
-     * @since 5.2.0.0
-     */
-    void registerParameterChangeListener(ParameterChangeListener listener);
-
-    /**
-     * Unregisters the given listener. Unregistering a listener that isn't registered is not an error.
-     * @since 5.2.0.0
-     */
-    void unregisterParameterChangeListener(ParameterChangeListener listener);
-
-    /**
-     * @return true if the parameter value should be cached within a component during rendering
-     * @since 5.2.0.0
-     */
-    boolean shouldCache();
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java Fri Jan 22 16:31:47 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -66,8 +66,8 @@
 
     private Map<String, Boolean> handledEvents;
 
-    public MutableComponentModelImpl(String componentClassName, Logger logger, Resource baseResource,
-                                     ComponentModel parentModel)
+    public MutableComponentModelImpl(String componentClassName, Logger logger,
+            Resource baseResource, ComponentModel parentModel)
     {
         this.componentClassName = componentClassName;
         this.logger = logger;
@@ -106,7 +106,8 @@
         return componentClassName;
     }
 
-    public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix, boolean cached)
+    public void addParameter(String name, boolean required, boolean allowNull,
+            String defaultBindingPrefix, boolean cached)
     {
         Defense.notBlank(name, "name");
         Defense.notBlank(defaultBindingPrefix, "defaultBindingPrefix");
@@ -117,36 +118,49 @@
             parameters = CollectionFactory.newCaseInsensitiveMap();
 
         if (parameters.containsKey(name))
-            throw new IllegalArgumentException(ModelMessages.duplicateParameter(name, componentClassName));
+            throw new IllegalArgumentException(ModelMessages.duplicateParameter(name,
+                    componentClassName));
 
-        parameters.put(name, new ParameterModelImpl(name, required, allowNull, defaultBindingPrefix,cached));
+        parameters.put(name, new ParameterModelImpl(name, required, allowNull,
+                defaultBindingPrefix, cached));
     }
 
-    public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix)
-    {
-        //assume /false/ for the default because:
-        //if the parameter is actually cached, the only effect will be to reduce that optimization in certain
-        //scenarios (mixin BindParameter).  But if the value is NOT cached but we say it is,
-        //we'll get incorrect behavior.
-        addParameter(name,required,allowNull,defaultBindingPrefix,false);
+    public void addParameter(String name, boolean required, boolean allowNull,
+            String defaultBindingPrefix)
+    {
+        // assume /false/ for the default because:
+        // if the parameter is actually cached, the only effect will be to reduce that optimization
+        // in certain
+        // scenarios (mixin BindParameter). But if the value is NOT cached but we say it is,
+        // we'll get incorrect behavior.
+        addParameter(name, required, allowNull, defaultBindingPrefix, false);
     }
 
     public ParameterModel getParameterModel(String parameterName)
     {
         ParameterModel result = InternalUtils.get(parameters, parameterName.toLowerCase());
 
-        if (result == null && parentModel != null) result = parentModel.getParameterModel(parameterName);
+        if (result == null && parentModel != null)
+            result = parentModel.getParameterModel(parameterName);
 
         return result;
     }
 
+    @Override
+    public boolean isFormalParameter(String parameterName)
+    {
+        return getParameterModel(parameterName) != null;
+    }
+
     public List<String> getParameterNames()
     {
         List<String> names = CollectionFactory.newList();
 
-        if (parameters != null) names.addAll(parameters.keySet());
+        if (parameters != null)
+            names.addAll(parameters.keySet());
 
-        if (parentModel != null) names.addAll(parentModel.getParameterNames());
+        if (parentModel != null)
+            names.addAll(parentModel.getParameterNames());
 
         Collections.sort(names);
 
@@ -158,19 +172,19 @@
         return InternalUtils.sortedKeys(parameters);
     }
 
-    public MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type, String componentClassName,
-                                                              boolean inheritInformalParameters, Location location)
+    public MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type,
+            String componentClassName, boolean inheritInformalParameters, Location location)
     {
         // TODO: Parent compent model? Or would we simply override the parent?
 
-        if (embeddedComponents == null) embeddedComponents = CollectionFactory.newCaseInsensitiveMap();
+        if (embeddedComponents == null)
+            embeddedComponents = CollectionFactory.newCaseInsensitiveMap();
         else if (embeddedComponents.containsKey(id))
-            throw new IllegalArgumentException(ModelMessages.duplicateComponentId(id, this.componentClassName));
+            throw new IllegalArgumentException(ModelMessages.duplicateComponentId(id,
+                    this.componentClassName));
 
-        MutableEmbeddedComponentModel embedded = new MutableEmbeddedComponentModelImpl(id, type, componentClassName,
-                                                                                       this.componentClassName,
-                                                                                       inheritInformalParameters,
-                                                                                       location);
+        MutableEmbeddedComponentModel embedded = new MutableEmbeddedComponentModelImpl(id, type,
+                componentClassName, this.componentClassName, inheritInformalParameters, location);
 
         embeddedComponents.put(id, embedded);
 
@@ -181,9 +195,11 @@
     {
         List<String> result = CollectionFactory.newList();
 
-        if (embeddedComponents != null) result.addAll(embeddedComponents.keySet());
+        if (embeddedComponents != null)
+            result.addAll(embeddedComponents.keySet());
 
-        if (parentModel != null) result.addAll(parentModel.getEmbeddedComponentIds());
+        if (parentModel != null)
+            result.addAll(parentModel.getEmbeddedComponentIds());
 
         Collections.sort(result);
 
@@ -194,7 +210,8 @@
     {
         EmbeddedComponentModel result = InternalUtils.get(embeddedComponents, componentId);
 
-        if (result == null && parentModel != null) result = parentModel.getEmbeddedComponentModel(componentId);
+        if (result == null && parentModel != null)
+            result = parentModel.getEmbeddedComponentModel(componentId);
 
         return result;
     }
@@ -203,9 +220,11 @@
     {
         String result = InternalUtils.get(persistentFields, fieldName);
 
-        if (result == null && parentModel != null) result = parentModel.getFieldPersistenceStrategy(fieldName);
+        if (result == null && parentModel != null)
+            result = parentModel.getFieldPersistenceStrategy(fieldName);
 
-        if (result == null) throw new IllegalArgumentException(ModelMessages.missingPersistentField(fieldName));
+        if (result == null)
+            throw new IllegalArgumentException(ModelMessages.missingPersistentField(fieldName));
 
         return result;
     }
@@ -219,7 +238,8 @@
     {
         String logicalFieldName = persistentFieldNameAllocator.allocateId(fieldName);
 
-        if (persistentFields == null) persistentFields = CollectionFactory.newMap();
+        if (persistentFields == null)
+            persistentFields = CollectionFactory.newMap();
 
         persistentFields.put(logicalFieldName, strategy);
 
@@ -233,13 +253,15 @@
 
     public void addMixinClassName(String mixinClassName, String... order)
     {
-        if (mixinClassNames == null) mixinClassNames = CollectionFactory.newList();
+        if (mixinClassNames == null)
+            mixinClassNames = CollectionFactory.newList();
 
         mixinClassNames.add(mixinClassName);
         if (order != null && order.length > 0)
         {
-            if (mixinOrders == null) mixinOrders = CollectionFactory.newCaseInsensitiveMap();
-            mixinOrders.put(mixinClassName,order);
+            if (mixinOrders == null)
+                mixinOrders = CollectionFactory.newCaseInsensitiveMap();
+            mixinOrders.put(mixinClassName, order);
         }
     }
 
@@ -247,9 +269,11 @@
     {
         List<String> result = CollectionFactory.newList();
 
-        if (mixinClassNames != null) result.addAll(mixinClassNames);
+        if (mixinClassNames != null)
+            result.addAll(mixinClassNames);
 
-        if (parentModel != null) result.addAll(parentModel.getMixinClassNames());
+        if (parentModel != null)
+            result.addAll(parentModel.getMixinClassNames());
 
         Collections.sort(result);
 
@@ -286,7 +310,8 @@
         Defense.notBlank(key, "key");
         Defense.notBlank(value, "value");
 
-        if (metaData == null) metaData = CollectionFactory.newCaseInsensitiveMap();
+        if (metaData == null)
+            metaData = CollectionFactory.newCaseInsensitiveMap();
 
         // TODO: Error if duplicate?
 
@@ -297,7 +322,8 @@
     {
         Defense.notNull(renderPhase, "renderPhase");
 
-        if (handledRenderPhases == null) handledRenderPhases = CollectionFactory.newSet();
+        if (handledRenderPhases == null)
+            handledRenderPhases = CollectionFactory.newSet();
 
         handledRenderPhases.add(renderPhase);
     }
@@ -314,7 +340,8 @@
     {
         String result = InternalUtils.get(metaData, key);
 
-        if (result == null && parentModel != null) result = parentModel.getMeta(key);
+        if (result == null && parentModel != null)
+            result = parentModel.getMeta(key);
 
         return result;
     }
@@ -334,15 +361,14 @@
 
     public boolean handlesEvent(String eventType)
     {
-        if (InternalUtils.get(handledEvents, eventType) != null) return true;
+        if (InternalUtils.get(handledEvents, eventType) != null)
+            return true;
 
-        return parentModel == null
-               ? false
-               : parentModel.handlesEvent(eventType);
+        return parentModel == null ? false : parentModel.handlesEvent(eventType);
     }
 
     public String[] getOrderForMixin(String mixinClassName)
     {
-        return InternalUtils.get(mixinOrders,mixinClassName);
+        return InternalUtils.get(mixinOrders, mixinClassName);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java Fri Jan 22 16:31:47 2010
@@ -16,6 +16,7 @@
 
 import javassist.CtClass;
 import org.apache.tapestry5.internal.structure.ComponentPageElement;
+import org.apache.tapestry5.internal.structure.InternalComponentResourcesImpl;
 import org.apache.tapestry5.internal.structure.Page;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.Messages;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Fri Jan 22 16:31:47 2010
@@ -68,13 +68,12 @@
 /**
  * Implements {@link RenderCommand} and represents a component within an overall page. Much of a
  * component page
- * element's behavior is delegated to user code, via a
- * {@link org.apache.tapestry5.runtime.Component} instance.
+ * element's behavior is delegated to user code, via a {@link org.apache.tapestry5.runtime.Component} instance.
  * <p/>
  * Once instantiated, a ComponentPageElement should be registered as a
  * {@linkplain org.apache.tapestry5.internal.structure.Page#addLifecycleListener(org.apache.tapestry5.runtime.PageLifecycleListener)
- * lifecycle listener}. This could be done inside the constructors, but that tends to complicate
- * unit tests, so its done by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}.
+ * lifecycle listener}. This could be done inside the constructors, but that tends to complicate unit tests, so its done
+ * by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}.
  */
 public class ComponentPageElementImpl extends BaseLocatable implements ComponentPageElement,
         PageLifecycleListener
@@ -249,9 +248,7 @@
     }
 
     /**
-     * Replaces
-     * {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl.BeginRenderPhase}
-     * when there is
+     * Replaces {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl.BeginRenderPhase} when there is
      * a handler for AfterRender but not BeginRender.
      */
     private class OptimizedBeginRenderPhase implements RenderCommand
@@ -515,8 +512,6 @@
 
     private Orderer<Component> mixinAfterOrderer;
 
-    private List<Runnable> deferredLoadActions;
-
     private boolean loaded;
 
     /**
@@ -588,7 +583,7 @@
                 .getComponentResources();
 
         coreResources = new InternalComponentResourcesImpl(this.page, this, containerResources,
-                this.elementResources, completeId, nestedId, instantiator);
+                this.elementResources, completeId, nestedId, instantiator, false);
 
         coreComponent = coreResources.getComponent();
 
@@ -713,7 +708,7 @@
 
         InternalComponentResourcesImpl resources = new InternalComponentResourcesImpl(page, this,
                 coreResources, elementResources, completeId + mixinExtension, nestedId
-                        + mixinExtension, instantiator);
+                        + mixinExtension, instantiator, true);
 
         mixinIdToComponentResources.put(mixinId, resources);
         // note that since we're using explicit ordering now,
@@ -833,21 +828,9 @@
         // that is invoked first, before we check for unbound parameters.
 
         invoke(false, CONTAINING_PAGE_DID_LOAD);
-        executeDeferredLoadActions();
         verifyRequiredParametersAreBound();
     }
 
-    private void executeDeferredLoadActions()
-    {
-        if (deferredLoadActions == null)
-            return;
-        for (Runnable action : deferredLoadActions)
-        {
-            action.run();
-        }
-        deferredLoadActions = null;// having executed them, we have no need now to store them.
-    }
-
     public void enqueueBeforeRenderBody(RenderQueue queue)
     {
         // If no body, then no beforeRenderBody or afterRenderBody
@@ -930,18 +913,6 @@
         return null;
     }
 
-    public boolean isMixingIn(String mixinClassName)
-    {
-        return mixinForClassName(mixinClassName) != null;
-    }
-
-    public void deferLoadAction(Runnable action)
-    {
-        if (deferredLoadActions == null)
-            deferredLoadActions = CollectionFactory.newList();
-        deferredLoadActions.add(action);
-    }
-
     public ComponentResources getMixinResources(String mixinId)
     {
         ComponentResources result = null;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java Fri Jan 22 16:31:47 2010
@@ -18,7 +18,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.tapestry5.Binding;
 import org.apache.tapestry5.Block;
@@ -29,8 +28,6 @@
 import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.internal.ParameterAccess;
-import org.apache.tapestry5.internal.ParameterChangeListener;
-import org.apache.tapestry5.internal.ParameterChangedEvent;
 import org.apache.tapestry5.internal.services.Instantiator;
 import org.apache.tapestry5.internal.transform.ParameterConduit;
 import org.apache.tapestry5.ioc.AnnotationProvider;
@@ -46,7 +43,6 @@
 import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 import org.apache.tapestry5.runtime.RenderQueue;
-import org.apache.tapestry5.services.FieldValueConduit;
 import org.slf4j.Logger;
 
 /**
@@ -81,7 +77,7 @@
     // Case-insensitive map from container-parameter name to ParameterAccess, for BindParameter.
     // Should only ever be used for mixins.
     private Map<String, ParameterAccess> containerParameterAccess;
-    
+
     // Case insentive map from parameter name to ParameterConduit, used to support mixins
     // which need access to the containing component's PC's
     private Map<String, ParameterConduit> conduits;
@@ -95,6 +91,8 @@
 
     private boolean informalsComputed;
 
+    private final boolean mixin;
+
     /**
      * We keep a linked list of informal parameters, which saves us the expense of determining which
      * bindings are formal
@@ -139,7 +137,7 @@
 
     public InternalComponentResourcesImpl(Page page, ComponentPageElement element,
             ComponentResources containerResources, ComponentPageElementResources elementResources,
-            String completeId, String nestedId, Instantiator componentInstantiator)
+            String completeId, String nestedId, Instantiator componentInstantiator, boolean mixin)
     {
         this.page = page;
         this.element = element;
@@ -147,11 +145,18 @@
         this.elementResources = elementResources;
         this.completeId = completeId;
         this.nestedId = nestedId;
+        this.mixin = mixin;
 
         componentModel = componentInstantiator.getModel();
         component = componentInstantiator.newInstance(this);
     }
 
+    @Override
+    public boolean isMixin()
+    {
+        return mixin;
+    }
+
     public Location getLocation()
     {
         return element.getLocation();
@@ -346,16 +351,6 @@
         return element.getMixinByClassName(mixinClassName);
     }
 
-    public boolean isMixingIn(String mixinClassName)
-    {
-        return element.isMixingIn(mixinClassName);
-    }
-
-    public void deferLoadAction(Runnable action)
-    {
-        element.deferLoadAction(action);
-    }
-
     public void renderInformalParameters(MarkupWriter writer)
     {
         if (bindings == null)
@@ -514,54 +509,6 @@
         return result;
     }
 
-    public ParameterAccess getContainerBoundParameterAccess(final String boundParameterName,
-            String... parentParameterNames)
-    {
-        if (containerParameterAccess == null)
-            containerParameterAccess = CollectionFactory.newCaseInsensitiveMap();
-
-        ParameterAccess result = containerParameterAccess.get(boundParameterName);
-        if (result == null)
-        {
-            final InternalComponentResources res = (InternalComponentResources) getContainerResources();
-            // Ideally, this check would occur at class fabrication time. But there's not currently
-            // a way
-            // to tell if a component class is a mixin class, short of checking for "mixins" in the
-            // FQCN.
-            // So we check to make sure that this component class name is in the set of mixins
-            // defined for the container
-            // resources.
-            if (!res.isMixingIn(this.getComponentModel().getComponentClassName()))
-            {
-                // then we're not a mixin, we're a component in the tree.
-                throw new TapestryException(StructureMessages.bindParameterOnlyOnMixin(
-                        boundParameterName, this), this, null);
-            }
-            // Have to be careful here. Problem is that if the mixin is not @MixinAfter, its
-            // PAGE_DID_LOAD will be called
-            // before the core component's. That can potentially result in missing default bindings
-            // if we
-            // call getParameterAcces at the wrong time (the unbound parameter access will be
-            // cached...).
-            String parentParameterName = findParentParameterName(parentParameterNames);
-            if (parentParameterName == null) { throw new TapestryException(StructureMessages
-                    .noSuchCoreComponentParameter(this, boundParameterName, parentParameterNames),
-                    this, null); }
-            result = createContainerParameterAccess(parentParameterName);
-            containerParameterAccess.put(boundParameterName, result);
-        }
-        return result;
-    }
-
-    private String findParentParameterName(String... queries)
-    {
-        for (String query : queries)
-        {
-            if (getContainerResources().getComponentModel().getParameterModel(query) != null) { return query; }
-        }
-        return null;
-    }
-
     private ParameterAccess createParameterAccess(final String parameterName)
     {
         final Binding binding = getBinding(parameterName);
@@ -580,13 +527,6 @@
                 return binding != null;
             }
 
-            public Object read(String desiredTypeName)
-            {
-                Class desiredType = elementResources.toClass(desiredTypeName);
-
-                return read(desiredType);
-            }
-
             public <T> T read(Class<T> desiredType)
             {
                 if (binding == null)
@@ -621,26 +561,16 @@
 
             public <T> void write(T parameterValue)
             {
-
                 if (binding == null)
-                {
-                    // have to fire in case there's a mixin watching value;
-                    // even if it's not bound to any other value,
-                    // the mixin needs to know that the value internal to the component
-                    // was changed.
-                    fireParameterChanged(parameterName, parameterValue);
                     return;
-                }
 
                 Class bindingType = binding.getBindingType();
-                
+
                 try
                 {
                     Object coerced = elementResources.coerce(parameterValue, bindingType);
 
                     binding.set(coerced);
-                
-                    fireParameterChanged(parameterName, coerced);
                 }
                 catch (Exception ex)
                 {
@@ -663,122 +593,7 @@
             {
                 return binding == null ? null : binding.getAnnotation(annotationClass);
             }
-
-            private Set<ParameterChangeListener> listeners;
-
-            public void registerParameterChangeListener(ParameterChangeListener listener)
-            {
-                Defense.notNull(listener, "listener");
-                if (listeners == null)
-                    listeners = CollectionFactory.newSet();
-                listeners.add(listener);
-            }
-
-            public void unregisterParameterChangeListener(ParameterChangeListener listener)
-            {
-                if (listeners == null)
-                    return;
-                listeners.remove(listener);
-            }
-
-            public boolean shouldCache()
-            {
-                return cache;
-            }
-
-            protected void fireParameterChanged(String parameterName, Object newValue)
-            {
-                ParameterChangedEvent event = new ParameterChangedEvent(parameterName, newValue);
-
-                if (listeners != null)
-                {
-                    for (ParameterChangeListener l : listeners)
-                    {
-                        l.parameterChanged(event);
-                    }
-                }
-            }
-
-        };
-    }
-
-    private ParameterAccess createContainerParameterAccess(final String parentParameterName)
-    {
-
-        return new ParameterAccess()
-        {
-
-            private ParameterAccess access()
-            {
-                return element.getComponentResources().getParameterAccess(parentParameterName);
-            }
-
-            public boolean isBound()
-            {
-                return element.getBinding(parentParameterName) != null;
-            }
-
-            public Object read(String desiredTypeName)
-            {
-                return access().read(desiredTypeName);
-            }
-
-            public <T> T read(Class<T> expectedType)
-            {
-                return access().read(expectedType);
-            }
-
-            public <T> void write(T parameterValue)
-            {
-                access().write(parameterValue);
-            }
-
-            public boolean isInvariant()
-            {
-                return access().isInvariant();
-            }
-
-            public Class getBoundType()
-            {
-                return access().getBoundType();
-            }
-
-            public void registerParameterChangeListener(final ParameterChangeListener listener)
-            {
-                // if it's not bound, try defering.
-                if (isBound())
-                {
-                    access().registerParameterChangeListener(listener);
-                }
-                else
-                {
-                    // try waiting for it. If it's not bound after load, then it's not bound at all.
-                    element.deferLoadAction(new Runnable()
-                    {
-                        public void run()
-                        {
-                            access().registerParameterChangeListener(listener);
-                        }
-                    });
-                }
-            }
-
-            public void unregisterParameterChangeListener(ParameterChangeListener listener)
-            {
-                access().unregisterParameterChangeListener(listener);
-            }
-
-            public boolean shouldCache()
-            {
-                return access().shouldCache();
-            }
-
-            public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
-            {
-                return access().getAnnotation(annotationClass);
-            }
         };
-
     }
 
     public void addPageResetListener(PageResetListener listener)
@@ -787,7 +602,7 @@
     }
 
     @Override
-    public FieldValueConduit getParameterConduit(String parameterName)
+    public ParameterConduit getParameterConduit(String parameterName)
     {
         return InternalUtils.get(conduits, parameterName);
     }
@@ -795,10 +610,10 @@
     @Override
     public void setParameterConduit(String parameterName, ParameterConduit conduit)
     {
-        if (conduits == null) conduits = CollectionFactory.newCaseInsensitiveMap();
+        if (conduits == null)
+            conduits = CollectionFactory.newCaseInsensitiveMap();
 
         conduits.put(parameterName, conduit);
     }
-    
-    
+
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java Fri Jan 22 16:31:47 2010
@@ -122,20 +122,4 @@
         return MESSAGES.get("persist-change-before-load-complete");
     }
 
-    static String noSuchCoreComponentParameter(ComponentResources mixinResources,
-                                               String boundId, String...parameters)
-    {
-        return MESSAGES.format("no-such-core-component-parameter",
-                boundId,
-                mixinResources.getComponentModel().getComponentClassName(),
-                mixinResources.getContainerResources().getComponentModel().getComponentClassName(),
-                InternalUtils.joinSorted(Arrays.asList(parameters)),
-                InternalUtils.joinSorted(Arrays.asList(mixinResources.getContainerResources().getComponentModel().getDeclaredParameterNames())));
-    }
-
-    public static String bindParameterOnlyOnMixin(String boundParameterName, InternalComponentResourcesImpl internalComponentResources)
-    {
-        return MESSAGES.format("bind-parameter-only-on-mixin",boundParameterName,
-                internalComponentResources.getComponentModel().getComponentClassName());
-    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java Fri Jan 22 16:31:47 2010
@@ -1,10 +1,10 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 2010 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
+// 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,
@@ -14,335 +14,168 @@
 
 package org.apache.tapestry5.internal.transform;
 
-import org.apache.tapestry5.services.*;
-import org.apache.tapestry5.model.MutableComponentModel;
+import java.util.List;
+
+import javax.xml.ws.ServiceMode;
+
+import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.annotations.BindParameter;
-import org.apache.tapestry5.internal.*;
-import org.apache.tapestry5.internal.bindings.LiteralBinding;
-import org.apache.tapestry5.ioc.util.BodyBuilder;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.corelib.pages.ServiceStatus;
+import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.services.ComponentClassCache;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.Binding;
-
-import java.util.List;
-import java.util.Iterator;
-import java.util.Arrays;
-import java.lang.reflect.Modifier;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentValueProvider;
+import org.apache.tapestry5.services.FieldValueConduit;
 
 /**
- * Responsible for identifying, via the {@link org.apache.tapestry5.annotations.BindParameter} annotation,
- * mixin fields that should be bound to a core-component parameter value.
- *
- * @since 5.2.0.0
+ * Responsible for identifying, via the {@link org.apache.tapestry5.annotations.BindParameter} annotation, mixin fields
+ * that should be bound to a core-component parameter value.
+ * 
+ * @since 5.2.0
  */
 public class BindParameterWorker implements ComponentClassTransformWorker
 {
+    private final TypeCoercer typeCoercer;
 
-    private static final String EQUAL_METHOD_NAME = BindParameterWorker.class.getName() + ".equal";
+    private final ComponentClassCache componentClassCache;
 
-    public void transform(final ClassTransformation transformation, MutableComponentModel model)
+    public BindParameterWorker(TypeCoercer typeCoercer, ComponentClassCache componentClassCache)
     {
-        List<String> fieldNames = transformation.findFieldsWithAnnotation(BindParameter.class);
-
-        for(String fieldName : fieldNames)
-        {
-            BindParameter annotation = transformation.getFieldAnnotation(fieldName, BindParameter.class);
-            convertFieldIntoContainerBoundParameter(fieldName, annotation, transformation, model);
-        }
-
+        this.typeCoercer = typeCoercer;
+        this.componentClassCache = componentClassCache;
     }
 
-    private void convertFieldIntoContainerBoundParameter(String name, BindParameter annotation, ClassTransformation transformation,
-                                           MutableComponentModel model)
+    public void transform(final ClassTransformation transformation, MutableComponentModel model)
     {
-        transformation.claimField(name, annotation);
-
-        String boundParameterName = getBoundParameterName(name, annotation.name());
-        String[] parentParameterNames = getParentParameterNames(name, annotation.value());
-
-
-        String type = transformation.getFieldType(name);
-
-        //we can't do this exactly the same as parameter. We can't know at transformation time which parameter
-        //this thing will be linked to, because it could be wired to any number of different components.
-        //So we have to wait until runtime to examine caching and whether we should cache, rather than
-        //constructing the class differently based on caching or not.
-        String cachedFieldName = transformation.addField(Modifier.PRIVATE, "boolean", name + "_cached");
-
-        String resourcesFieldName = transformation.getResourcesFieldName();
+        List<String> fieldNames = transformation.findFieldsWithAnnotation(BindParameter.class);
 
-        String accessFieldName = addBoundParameterSetup(name,
-                boundParameterName, parentParameterNames,
-                cachedFieldName, type, resourcesFieldName,
-                transformation);
+        for (String fieldName : fieldNames)
+        {
+            BindParameter annotation = transformation.getFieldAnnotation(fieldName,
+                    BindParameter.class);
 
-        addReaderMethod(name, cachedFieldName, accessFieldName, boundParameterName, type, resourcesFieldName,
-                        transformation);
+            convertFieldIntoContainerBoundParameter(fieldName, annotation, transformation);
+        }
 
-        addWriterMethod(name, cachedFieldName, accessFieldName, boundParameterName, type, resourcesFieldName,
-                        transformation);
     }
 
-
-    /**
-     * Returns the name of a field that stores whether the parameter binding is invariant.
-     */
-    private String addBoundParameterSetup(String fieldName, String boundParameterName, String[] parentParameterNames,
-                                     String cachedFieldName, String fieldType,
-                                     String resourcesFieldName, ClassTransformation transformation)
+    private void convertFieldIntoContainerBoundParameter(final String fieldName,
+            final BindParameter annotation, ClassTransformation transformation)
     {
-        String accessFieldName = transformation.addField(Modifier.PRIVATE, ParameterAccess.class.getName(),
-                fieldName + "_access");
-
-        String parentNamesField = transformation.addField(Modifier.PRIVATE, String[].class.getName(),
-                fieldName + "_parentparameternames");
-
-        String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName + "_default");
-
-        BodyBuilder builder = new BodyBuilder().begin();
+        final String fieldTypeName = transformation.getFieldType(fieldName);
 
-        builder.addln("%s = new String[%d];",parentNamesField,parentParameterNames.length);
+        transformation.claimField(fieldName, annotation);
 
-        for(int i=0;i<parentParameterNames.length;i++)
+        ComponentValueProvider<FieldValueConduit> provider = new ComponentValueProvider<FieldValueConduit>()
         {
-            builder.addln("%s[%d]=\"%s\";",parentNamesField,i,parentParameterNames[i]);
-        }
-
-        builder.addln("%s = %s.getContainerBoundParameterAccess(\"%s\",%s);",
-                accessFieldName,
-                resourcesFieldName,
-                boundParameterName,
-                parentNamesField);
-
-        // Store the current value of the field into the default field. This value will
-        // be used to reset the field after rendering.
-
-        builder.addln("%s = %s;", defaultFieldName, fieldName);
-
 
-        addListenerSetup(fieldName, fieldType, boundParameterName, parentParameterNames, accessFieldName,  builder,
-                         transformation);
-
-        builder.end();
-
-        transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, builder
-                .toString());
-
-        // Now, when the component completes rendering, ensure that any variant parameters are
-        // are returned to default value. This isn't necessary when the parameter is not cached,
-        // because (unless the binding is invariant), there's no value to get rid of (and if it is
-        // invariant, there's no need to get rid of it).
-        // as with reader/writer methods, we have to do the caching check at runtime.
-        builder.clear();
-
-        builder.addln("if (%s.shouldCache() && ! %1$s.isInvariant())", accessFieldName);
-        builder.begin();
-        builder.addln("%s = %s;", fieldName, defaultFieldName);
-        builder.addln("%s = false;", cachedFieldName);
-        builder.end();
-
-        // Clean up after the component renders.
-
-        String body = builder.toString();
-
-        transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, body);
-
-        // And again, when the page is detached (TAPESTRY-2460)
-
-        transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE, body);
-
-        return accessFieldName;
-    }
-
-    private void addListenerSetup(
-            String fieldName,
-            String fieldType,
-            String boundParameterName,
-            String[] parentParameterNames,
-            String accessFieldName,
-            BodyBuilder builder,
-            ClassTransformation transformation)
+            @Override
+            public FieldValueConduit get(final ComponentResources resources)
+            {
+                if (!resources.isMixin())
+                    throw new TapestryException(TransformMessages.bindParameterOnlyOnMixin(
+                            fieldName, resources), null);
+
+                final InternalComponentResources containerResources = (InternalComponentResources) resources
+                        .getContainerResources();
+
+                // Evaluate this early so that we get a fast fail.
+
+                final String containerParameterName = identifyParameterName(resources,
+                        InternalUtils.stripMemberName(fieldName), annotation.value());
+
+                final Class fieldType = componentClassCache.forName(fieldTypeName);
+
+                return new FieldValueConduit()
+                {
+                    private ParameterConduit conduit;
+
+                    /**
+                     * Defer obtaining the conduit object until needed, to deal with the complex
+                     * lifecycle of
+                     * parameters. Perhaps this can be addressed by converting constructors into
+                     * methods invoked
+                     * from the page loaded lifecycle method?
+                     */
+                    private ParameterConduit getParameterConduit()
+                    {
+                        if (conduit == null)
+                        {
+                            conduit = containerResources
+                                    .getParameterConduit(containerParameterName);
+
+                        }
+
+                        return conduit;
+                    }
+
+                    @Override
+                    public void set(Object newValue)
+                    {
+                        getParameterConduit().set(newValue);
+                    }
+
+                    @SuppressWarnings("unchecked")
+                    @Override
+                    public Object get()
+                    {
+                        // For the moment, this results in two passes through the TypeCoercer; we'll look
+                        // to optimize that in the future. The first pass is deep inside ParameterConduit (coercing
+                        // to the component parameter field type), the second is here (usually the same type so no
+                        // real coercion necessary).
+
+                        Object result = getParameterConduit().get();
+
+                        return typeCoercer.coerce(result, fieldType);
+                    }
+                };
+            }
+        };
+
+        transformation.replaceFieldAccess(fieldName, provider);
+    }
+
+    private String identifyParameterName(ComponentResources resources, String firstGuess,
+            String... otherGuesses)
     {
-        transformation.addImplementedInterface(ParameterChangeListener.class);
-        builder.addln("%s.registerParameterChangeListener($0);",accessFieldName);
+        ComponentModel model = resources.getContainerResources().getComponentModel();
 
-        TransformMethodSignature signature = new TransformMethodSignature(Modifier.PUBLIC, "void", "parameterChanged",
-                new String[] {ParameterChangedEvent.class.getName()}, null);
+        List<String> guesses = CollectionFactory.newList();
+        guesses.add(firstGuess);
 
-        BodyBuilder changedBody = new BodyBuilder().begin();
-        //by this point, we know that there is at least one entry in parent Parameter Names.
-        changedBody.add("if (%s($1, \"%s\")", EQUAL_METHOD_NAME, parentParameterNames[0]);
-        for(int i=1; i<parentParameterNames.length; i++)
+        for (String name : otherGuesses)
         {
-            changedBody.add(" || %s($1, \"%s\")", EQUAL_METHOD_NAME, parentParameterNames[i]);
+            guesses.add(name);
         }
-        changedBody.add(")").begin();
-
-        String cast = TransformUtils.getWrapperTypeName(fieldType);
-
-        if (TransformUtils.isPrimitive(fieldType))
-            changedBody.addln("%s = ((%s) $1.getNewValue()).%s();",
-                    fieldName, cast, TransformUtils.getUnwrapperMethodName(fieldType));
-        else
-            changedBody.addln("%s = (%s) $1.getNewValue();",fieldName, cast);
-
-        changedBody.addln("return;").end();
-
-        changedBody.end();
-
-        transformation.extendMethod(signature,changedBody.toString());
-
-    }
-
-    private void addWriterMethod(String fieldName, String cachedFieldName, String accessFieldName,
-                                 String boundParameterName, String fieldType,
-                                 String resourcesFieldName, ClassTransformation transformation)
-    {
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-
-        // Before the component is loaded, updating the property sets the default value
-        // for the parameter. The value is stored in the field, but will be
-        // rolled into default field inside containingPageDidLoad().
-
-        builder.addln("if (! %s.isLoaded())", resourcesFieldName);
-        builder.begin();
-        builder.addln("%s = $1;", fieldName);
-        builder.addln("return;");
-        builder.end();
-
-        //unregistering the listener from the parameter change listener list avoids double-setting the field,
-        builder.addln("%s.unregisterParameterChangeListener($0);",accessFieldName);
-
-        // Always start by updating the parameter; this will implicitly check for
-        // read-only or unbound parameters. $1 is the single parameter
-        // to the method.
-        builder.addln("%s.write(($w)$1);", accessFieldName);
-        builder.addln("%s = $1;",fieldName);
-        builder.addln("%s.registerParameterChangeListener($0);",accessFieldName);
-
-        //note that there's no way of knowing at class transformation time which component a mixin will
-        //be associated with and, further more, no way of knowing which @Parameter a mixin field will be
-        //@BindParameter'ed to.  So we have to generate caching code that works at runtime, rather than
-        //including or not including caching logic at transformation time.
-        builder.addln("if (%s.shouldCache())",accessFieldName).begin();
-        builder.addln("%s = %s.isRendering();",cachedFieldName, resourcesFieldName).end();
-        builder.end();
-
-        String methodName = transformation.newMemberName("update_boundparameter", boundParameterName);
-
-        TransformMethodSignature signature = new TransformMethodSignature(Modifier.PRIVATE, "void", methodName,
-                new String[] {fieldType}, null);
-
-        transformation.addMethod(signature, builder.toString());
-
-        builder.clear();
-
-        //add the catch because if we don't re-register the class as a parameter change listener, it's value
-        //could wind up stale, and write can throw an exception.
-        builder.begin();
-        builder.addln("%s.registerParameterChangeListener($0);", accessFieldName);
-        builder.addln("throw $e;");
-        builder.end();
-
-        transformation.addCatch(signature,Exception.class.getName(),builder.toString());
-
-        transformation.replaceWriteAccess(fieldName, methodName);
-    }
-
-    /**
-     * Adds a private method that will be the replacement for read-access to the field.
-     */
-    private void addReaderMethod(String fieldName, String cachedFieldName, String accessFieldName,
-                                 String boundParameterName, String fieldType, String resourcesFieldName,
-                                 ClassTransformation transformation)
-    {
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-
-        // While the component is still loading, or when the value for the component is cached,
-        // or if the value is not bound, then return the current value of the field.
-
-        builder.addln("if ((%s.shouldCache() && %s) || ! %s.isLoaded() || ! %s.isBound()) return %s;",
-                accessFieldName, cachedFieldName, resourcesFieldName, accessFieldName, fieldName);
-
-        String cast = TransformUtils.getWrapperTypeName(fieldType);
-
-        // The ($r) cast will convert the result to the method return type; generally
-        // this does nothing. but for primitive types, it will unwrap
-        // the wrapper type back to a primitive.  We pass the desired type name
-        // to readParameter(), since its easier to convert it properly to
-        // a type on that end than in the generated code.
-
-        builder.addln("%s result = ($r) ((%s) %s.read(\"%2$s\"));", fieldType, cast, accessFieldName);
-
-        // If the binding is invariant, then it's ok to cache. Othewise, its only
-        // ok to cache if a) the @Parameter says to cache and b) the component
-        // is rendering at the point when field is accessed.
 
-        builder.add("if (%s.isInvariant() || (%1$s.shouldCache() && %s.isRendering()))",
-                accessFieldName, resourcesFieldName);
-
-        builder.begin();
-        builder.addln("%s = result;", fieldName);
-        builder.addln("%s = true;", cachedFieldName);
-        builder.end();
-
-        builder.addln("return result;");
-        builder.end();
-
-        String methodName = transformation.newMemberName("read_boundparameter", boundParameterName);
-
-        TransformMethodSignature signature = new TransformMethodSignature(Modifier.PRIVATE, fieldType, methodName, null,
-                null);
-
-        transformation.addMethod(signature, builder.toString());
-
-        transformation.replaceReadAccess(fieldName, methodName);
-    }
-
-    private String getBoundParameterName(String fieldName, String annotatedName)
-    {
-        if (InternalUtils.isNonBlank(annotatedName)) return annotatedName;
-
-        return InternalUtils.stripMemberName(fieldName);
-    }
-
-    private String[] getParentParameterNames(String fieldName, String... names)
-    {
-        List<String> temp = CollectionFactory.newList(names);
-        for(Iterator<String> it = temp.iterator();it.hasNext(); )
+        for (String name : guesses)
         {
-            String name =it.next();
-            if (InternalUtils.isBlank(name)) it.remove();
+            if (model.isFormalParameter(name))
+                return name;
         }
-        if (temp.isEmpty())
-            return new String[] {InternalUtils.stripMemberName(fieldName)};
 
-        return temp.toArray(new String[temp.size()]);
-    }
+        String message = String
+                .format(
+                        "Failed to bind parameter of mixin %s (type %s). Containing component %s does not contain a formal parameter %s %s. Formal parameters: %s.",
+                        resources.getCompleteId(),
 
-    /**
-     * Invoked from generated code as part of the handling of parameter default methods.
-     */
-    public static void bind(String parameterName, InternalComponentResources resources, Object value)
-    {
-        if (value == null) return;
+                        resources.getComponentModel().getComponentClassName(),
 
-        if (value instanceof Binding)
-        {
-            Binding binding = (Binding) value;
+                        model.getComponentClassName(),
 
-            resources.bindParameter(parameterName, binding);
-            return;
-        }
+                        guesses.size() == 1 ? "matching" : "matching any of",
 
-        resources.bindParameter(parameterName, new LiteralBinding(null, "default " + parameterName, value));
-    }
+                        InternalUtils.joinSorted(guesses),
 
-    public static <T> boolean equal(T left, T right)
-    {
-        return TapestryInternalUtils.isEqual(left,right);
-    }
+                        InternalUtils.joinSorted(model.getDeclaredParameterNames()));
 
+        throw new TapestryException(message, resources.getLocation(), null);
+    }
 }