You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2006/12/30 02:08:41 UTC

svn commit: r491145 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/annotations/ main/java/org/apache/tapestry/internal/bindings/ main/java/org/apache/tapestry/internal/services/ main/java/...

Author: hlship
Date: Fri Dec 29 17:08:39 2006
New Revision: 491145

URL: http://svn.apache.org/viewvc?view=rev&rev=491145
Log:
Support a value() attribute in the Parameter annotation, the default binding expression for the parameter if otherwise unbound

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ParameterDefault.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/DefaultParameterComponent.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterDefault.html
Removed:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassFactoryImpl.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Parameter.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MethodSignature.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Echo.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLocatorTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java Fri Dec 29 17:08:39 2006
@@ -1,3 +1,17 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package org.apache.tapestry;
 
 import java.util.List;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java Fri Dec 29 17:08:39 2006
@@ -1,3 +1,17 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package org.apache.tapestry;
 
 import java.io.Serializable;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java Fri Dec 29 17:08:39 2006
@@ -1,3 +1,17 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package org.apache.tapestry;
 
 import java.io.ByteArrayInputStream;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Parameter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Parameter.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Parameter.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Parameter.java Fri Dec 29 17:08:39 2006
@@ -12,47 +12,49 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Annotation placed on a field to indicate that it is, in fact, an parameter. Parameters may be
- * optional or required. Required parameters must be bound.
- * <p>
- * TODO: Do we want to support a notNull as well as required?
- * 
- * 
- */
-@Target(FIELD)
-@Documented
-@Retention(RUNTIME)
-public @interface Parameter {
-
-    /**
-     * The name of the parameter. If not specified, the name of the parameter is derived from the
-     * name of the field (after stripping off leading punctuation) from the field name.
-     */
-    String name() default "";
-
-    /**
-     * If true, the parameter is required and and must be bound. If false (the default), then the
-     * parameter is optional.
-     */
-    boolean required() default false;
-
-    /**
-     * If true (the default), then the value for the parameter is cached while the component is,
-     * itself, rendering. Values from invariant bindings (such as literal strings) are always
-     * cached, regardless of this setting. Set this attribute to false to force the parameter to be
-     * {@link org.apache.tapestry.services.Binding#get() re-read} every time the field is accessed, even
-     * while the component is rendering.
-     */
-
-    boolean cache() default true;
-}
+package org.apache.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation placed on a field to indicate that it is, in fact, an parameter. Parameters may be
+ * optional or required. Required parameters must be bound.
+ */
+@Target(FIELD)
+@Documented
+@Retention(RUNTIME)
+public @interface Parameter {
+
+    /**
+     * The name of the parameter. If not specified, the name of the parameter is derived from the
+     * name of the field (after stripping off leading punctuation) from the field name.
+     */
+    String name() default "";
+
+    /**
+     * If true, the parameter is required and and must be bound. If false (the default), then the
+     * parameter is optional.
+     */
+    boolean required() default false;
+
+    /**
+     * If true (the default), then the value for the parameter is cached while the component is,
+     * itself, rendering. Values from invariant bindings (such as literal strings) are always
+     * cached, regardless of this setting. Set this attribute to false to force the parameter to be
+     * {@link org.apache.tapestry.services.Binding#get() re-read} every time the field is accessed,
+     * even while the component is rendering.
+     */
+
+    boolean cache() default true;
+
+    /**
+     * The default value for the parameter if not bound (at not the empty string). This is a binding
+     * expression, typically the name of a property of the component to bind.
+     */
+    String value() default "";
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java Fri Dec 29 17:08:39 2006
@@ -133,7 +133,7 @@
 
     private BindingConstructor createConstructor(String key, Class targetClass, String propertyPath)
     {
-        // Race condition: simulataneous calls to fillCache() for the same targetClass/propertyName
+        // Race condition: simulataneous calls to createConstructor() for the same targetClass/propertyName
         // combination may result in duplicate binding classes being created, which causes no great
         // harm.
 
@@ -178,7 +178,7 @@
 
         if (adapter == null)
             throw new RuntimeException(BindingsMessages.noSuchProperty(
-                    targetClass,
+                    step,
                     terminalName,
                     propertyPath));
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSource.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSource.java Fri Dec 29 17:08:39 2006
@@ -12,60 +12,60 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import org.apache.tapestry.internal.event.InvalidationEventHub;
-
-/**
- * Creates {@link org.apache.tapestry.internal.services.Instantiator}s for components, based on
- * component class name. This will involve transforming the component's class before it is loaded.
- * <p>
- * In addition, a source acts as an event hub for
- * {@link org.apache.tapestry.events.InvalidationListener}s, so that any information derived from
- * loaded classes can be discarded and rebuilt when classes change.
- * <p>
- * The strategy used is that when <em>any</em> class (in a controlled package) changes, the entire
- * class loader is discarded, along with any instances derived from those classes. A new class
- * loader is created, and then invalidation events are fired to listeners.
- */
-public interface ComponentInstantiatorSource extends InvalidationEventHub
-{
-
-    /**
-     * Given the name of a component class, provides an instantiator for that component.
-     * Instantiators are cached, so repeated calls to this method with the same class name will
-     * return the same instance; however, callers should also be aware that the instantiators may
-     * lose validity after an invalidation (caused by changes to external Java class files).
-     * 
-     * @param classname
-     *            FQCN to find (and perhaps transform and load)
-     * @return an object which can instantiate an instance of the component
-     */
-    Instantiator findInstantiator(String classname);
-
-    /**
-     * Adds a controlled package. Only classes within controlled packages are subject to
-     * transformation.
-     * 
-     * @param packageName
-     *            the package name to add (must not be blank)
-     */
-    void addPackage(String packageName);
-
-    /**
-     * Checks to see if a fully qualfied class name exists.
-     * 
-     * @param className
-     *            name of class to check
-     * @return true if the class exists (there's a ".class" file), false otherwise
-     */
-    boolean exists(String className);
-
-    /**
-     * Returns a class loader with visibility to the transformed classes. Caution: callers of this
-     * should also listen for invalidation events and re-acquire the class loader after an
-     * invalidation.
-     */
-
-    ClassLoader getClassLoader();
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.internal.event.InvalidationEventHub;
+import org.apache.tapestry.ioc.services.ClassFactory;
+
+/**
+ * Creates {@link org.apache.tapestry.internal.services.Instantiator}s for components, based on
+ * component class name. This will involve transforming the component's class before it is loaded.
+ * <p>
+ * In addition, a source acts as an event hub for
+ * {@link org.apache.tapestry.events.InvalidationListener}s, so that any information derived from
+ * loaded classes can be discarded and rebuilt when classes change.
+ * <p>
+ * The strategy used is that when <em>any</em> class (in a controlled package) changes, the entire
+ * class loader is discarded, along with any instances derived from those classes. A new class
+ * loader is created, and then invalidation events are fired to listeners.
+ */
+public interface ComponentInstantiatorSource extends InvalidationEventHub
+{
+
+    /**
+     * Given the name of a component class, provides an instantiator for that component.
+     * Instantiators are cached, so repeated calls to this method with the same class name will
+     * return the same instance; however, callers should also be aware that the instantiators may
+     * lose validity after an invalidation (caused by changes to external Java class files).
+     * 
+     * @param classname
+     *            FQCN to find (and perhaps transform and load)
+     * @return an object which can instantiate an instance of the component
+     */
+    Instantiator findInstantiator(String classname);
+
+    /**
+     * Adds a controlled package. Only classes within controlled packages are subject to
+     * transformation.
+     * 
+     * @param packageName
+     *            the package name to add (must not be blank)
+     */
+    void addPackage(String packageName);
+
+    /**
+     * Checks to see if a fully qualfied class name exists.
+     * 
+     * @param className
+     *            name of class to check
+     * @return true if the class exists (there's a ".class" file), false otherwise
+     */
+    boolean exists(String className);
+
+    /**
+     * Returns a class factory that can be used to generate additional classes around enhanced
+     * classes, or create subclasses of enhanced classes.
+     */
+    ClassFactory getClassFactory();
+
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSourceImpl.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentInstantiatorSourceImpl.java Fri Dec 29 17:08:39 2006
@@ -34,7 +34,10 @@
 import org.apache.tapestry.events.UpdateListener;
 import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry.internal.util.URLChangeTracker;
+import org.apache.tapestry.ioc.internal.services.ClassFactoryClassPool;
+import org.apache.tapestry.ioc.internal.services.ClassFactoryImpl;
 import org.apache.tapestry.ioc.internal.util.Defense;
+import org.apache.tapestry.ioc.services.ClassFactory;
 
 /**
  * A wrapper around a Javassist class loader that allows certain classes to be modified as they are
@@ -49,7 +52,7 @@
 
     private final ClassLoader _parent;
 
-    private ClassPool _classPool;
+    private ClassFactoryClassPool _classPool;
 
     private Loader _loader;
 
@@ -57,6 +60,8 @@
 
     private final Log _log;
 
+    private ClassFactory _classFactory;
+
     /** Map from class name to Instantiator. */
     private final Map<String, Instantiator> _instantiatorMap = newMap();
 
@@ -90,12 +95,6 @@
         initializeService();
     }
 
-    /** @return the class loader used when loading enhanced/modified classes */
-    public ClassLoader getClassLoader()
-    {
-        return _loader;
-    }
-
     public synchronized void checkForUpdates()
     {
         if (!_changeTracker.containsChanges())
@@ -121,13 +120,13 @@
      */
     private void initializeService()
     {
-        _classPool = new ClassPool();
+        _classPool = new ClassFactoryClassPool(_parent);
 
-        ClassPath path = new LoaderClassPath(_parent);
+        _loader = new PackageAwareLoader(_parent, _classPool);
 
-        _classPool.appendClassPath(path);
+        ClassPath path = new LoaderClassPath(_loader);
 
-        _loader = new PackageAwareLoader(_parent, _classPool);
+        _classPool.appendClassPath(path);
 
         try
         {
@@ -137,6 +136,8 @@
         {
             throw new RuntimeException(ex);
         }
+
+        _classFactory = new ClassFactoryImpl(_classPool.getLoader(), _classPool, _log);
     }
 
     // This is called from well within a synchronized block.
@@ -276,5 +277,10 @@
         String path = className.replace(".", "/") + ".class";
 
         return _parent.getResource(path) != null;
+    }
+
+    public ClassFactory getClassFactory()
+    {
+        return _classFactory;
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ParameterWorker.java Fri Dec 29 17:08:39 2006
@@ -12,245 +12,275 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import java.lang.reflect.Modifier;
-import java.util.List;
-
-import org.apache.tapestry.annotations.Parameter;
+package org.apache.tapestry.internal.services;
+
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.util.BodyBuilder;
-import org.apache.tapestry.model.MutableComponentModel;
-import org.apache.tapestry.services.ClassTransformation;
-import org.apache.tapestry.services.ComponentClassTransformWorker;
-import org.apache.tapestry.services.MethodSignature;
-import org.apache.tapestry.services.TransformConstants;
-import org.apache.tapestry.services.TransformUtils;
-
-/**
- * Responsible for identifying parameters via the {@link org.apache.tapestry.annotations.Parameter}
- * annotation on component fields. This is one of the most complex of the transformations.
- * 
- * 
- */
-public class ParameterWorker implements ComponentClassTransformWorker
-{
-    public void transform(ClassTransformation transformation, MutableComponentModel model)
-    {
-        List<String> fieldNames = transformation.findFieldsWithAnnotation(Parameter.class);
-
-        for (String name : fieldNames)
-        {
-            convertFieldIntoParameter(name, transformation, model);
-        }
-    }
-
-    private void convertFieldIntoParameter(String name, ClassTransformation transformation,
-            MutableComponentModel model)
-    {
-        Parameter annotation = transformation.getFieldAnnotation(name, Parameter.class);
-
-        String parameterName = getParameterName(name, annotation.name());
-
-        model.addParameter(parameterName, annotation.required());
-
-        String type = transformation.getFieldType(name);
-
-        boolean cache = annotation.cache();
-
-        String cachedFieldName = transformation.addField(Modifier.PRIVATE, "boolean", name
-                + "_cached");
-
-        String resourcesFieldName = transformation.getResourcesFieldName();
-
-        String invariantFieldName = addParameterSetup(
-                name,
-                parameterName,
-                cachedFieldName,
-                cache,
-                type,
-                resourcesFieldName,
-                transformation);
-
-        addReaderMethod(
-                name,
-                cachedFieldName,
-                invariantFieldName,
-                cache,
-                parameterName,
-                type,
-                resourcesFieldName,
-                transformation);
-
-        addWriterMethod(
-                name,
-                cachedFieldName,
-                cache,
-                parameterName,
-                type,
-                resourcesFieldName,
-                transformation);
-
-        transformation.claimField(name, annotation);
-    }
-
-    /** Returns the name of a field that stores whether the parameter binding is invariant. */
-
-    private String addParameterSetup(String fieldName, String parameterName,
-            String cachedFieldName, boolean cache, String fieldType, String resourcesFieldName,
-            ClassTransformation transformation)
-    {
-        String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName
-                + "_default");
-
-        String invariantFieldName = transformation.addField(Modifier.PRIVATE, "boolean", fieldName
-                + "_invariant");
-
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-        builder.addln(
-                "%s = %s.isInvariant(\"%s\");",
-                invariantFieldName,
-                resourcesFieldName,
-                parameterName);
-
-        // 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);
-        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).
-
-        if (cache)
-        {
-            builder.clear();
-
-            builder.addln("if (! %s)", invariantFieldName);
-            builder.begin();
-            builder.addln("%s = %s;", fieldName, defaultFieldName);
-            builder.addln("%s = false;", cachedFieldName);
-            builder.end();
-
-            transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, builder
-                    .toString());
-        }
-
-        return invariantFieldName;
-    }
-
-    private void addWriterMethod(String fieldName, String cachedFieldName, boolean cache,
-            String parameterName, 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();
-
-        // 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("if (%s.isBound(\"%s\"))", resourcesFieldName, parameterName);
-        builder.addln("  %s.writeParameter(\"%s\", ($w)$1);", resourcesFieldName, parameterName);
-
-        builder.addln("%s = $1;", fieldName);
-
-        if (cache)
-            builder.addln("%s = %s.isRendering();", cachedFieldName, resourcesFieldName);
-
-        builder.end();
-
-        String methodName = transformation.newMemberName("_update_parameter_" + parameterName);
-
-        MethodSignature signature = new MethodSignature(Modifier.PRIVATE, "void", methodName,
-                new String[]
-                { fieldType }, null);
-
-        transformation.addMethod(signature, 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 invariantFieldName, boolean cache, String parameterName, 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 || ! %s.isLoaded() || ! %<s.isBound(\"%s\")) return %s;",
-                cachedFieldName,
-                resourcesFieldName,
-                parameterName,
-                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.
-
-        builder.addln(
-                "%s result = ($r) ((%s) %s.readParameter(\"%s\", $type));",
-                fieldType,
-                cast,
-                resourcesFieldName,
-                parameterName);
-
-        // 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", invariantFieldName);
-
-        if (cache)
-            builder.add(" || %s.isRendering()", resourcesFieldName);
-
-        builder.addln(")");
-        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_parameter_" + parameterName);
-
-        MethodSignature signature = new MethodSignature(Modifier.PRIVATE, fieldType, methodName,
-                null, null);
-
-        transformation.addMethod(signature, builder.toString());
-
-        transformation.replaceReadAccess(fieldName, methodName);
-    }
-
-    private String getParameterName(String fieldName, String annotatedName)
-    {
-        if (InternalUtils.isNonBlank(annotatedName))
-            return annotatedName;
-
-        return InternalUtils.stripMemberPrefix(fieldName);
-    }
-}
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+import org.apache.tapestry.services.MethodSignature;
+import org.apache.tapestry.services.TransformConstants;
+import org.apache.tapestry.services.TransformUtils;
+
+/**
+ * Responsible for identifying parameters via the {@link org.apache.tapestry.annotations.Parameter}
+ * annotation on component fields. This is one of the most complex of the transformations.
+ */
+public class ParameterWorker implements ComponentClassTransformWorker
+{
+    private final BindingSource _bindingSource;
+
+    public ParameterWorker(BindingSource bindingSource)
+    {
+        _bindingSource = bindingSource;
+    }
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        List<String> fieldNames = transformation.findFieldsWithAnnotation(Parameter.class);
+
+        for (String name : fieldNames)
+        {
+            convertFieldIntoParameter(name, transformation, model);
+        }
+    }
+
+    private void convertFieldIntoParameter(String name, ClassTransformation transformation,
+            MutableComponentModel model)
+    {
+        Parameter annotation = transformation.getFieldAnnotation(name, Parameter.class);
+
+        String parameterName = getParameterName(name, annotation.name());
+
+        model.addParameter(parameterName, annotation.required());
+
+        String type = transformation.getFieldType(name);
+
+        boolean cache = annotation.cache();
+
+        String cachedFieldName = transformation.addField(Modifier.PRIVATE, "boolean", name
+                + "_cached");
+
+        String resourcesFieldName = transformation.getResourcesFieldName();
+
+        String invariantFieldName = addParameterSetup(
+                name,
+                annotation.value(),
+                parameterName,
+                cachedFieldName,
+                cache,
+                type,
+                resourcesFieldName,
+                transformation);
+
+        addReaderMethod(
+                name,
+                cachedFieldName,
+                invariantFieldName,
+                cache,
+                parameterName,
+                type,
+                resourcesFieldName,
+                transformation);
+
+        addWriterMethod(
+                name,
+                cachedFieldName,
+                cache,
+                parameterName,
+                type,
+                resourcesFieldName,
+                transformation);
+
+        transformation.claimField(name, annotation);
+    }
+
+    /**
+     * Returns the name of a field that stores whether the parameter binding is invariant.
+     */
+    private String addParameterSetup(String fieldName, String defaultBinding, String parameterName,
+            String cachedFieldName, boolean cache, String fieldType, String resourcesFieldName,
+            ClassTransformation transformation)
+    {
+        String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName
+                + "_default");
+
+        String invariantFieldName = transformation.addField(Modifier.PRIVATE, "boolean", fieldName
+                + "_invariant");
+
+        BodyBuilder builder = new BodyBuilder();
+        builder.begin();
+
+        if (InternalUtils.isNonBlank(defaultBinding))
+        {
+            builder.addln("if (! %s.isBound(\"%s\"))", resourcesFieldName, parameterName);
+
+            String bindingFactoryFieldName = transformation.addInjectedField(
+                    BindingSource.class,
+                    "bindingSource",
+                    _bindingSource);
+
+            builder
+                    .addln(
+                            " %s.addParameter(\"%s\", %s.newBinding(\"default %2$s\", %1$s, %1$s, \"%s\", \"%s\", null));",
+                            resourcesFieldName,
+                            parameterName,
+                            bindingFactoryFieldName,
+                            InternalConstants.PROP_BINDING_PREFIX,
+                            defaultBinding);
+
+        }
+
+        builder.addln(
+                "%s = %s.isInvariant(\"%s\");",
+                invariantFieldName,
+                resourcesFieldName,
+                parameterName);
+
+        // 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);
+        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).
+
+        if (cache)
+        {
+            builder.clear();
+
+            builder.addln("if (! %s)", invariantFieldName);
+            builder.begin();
+            builder.addln("%s = %s;", fieldName, defaultFieldName);
+            builder.addln("%s = false;", cachedFieldName);
+            builder.end();
+
+            transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, builder
+                    .toString());
+        }
+
+        return invariantFieldName;
+    }
+
+    private void addWriterMethod(String fieldName, String cachedFieldName, boolean cache,
+            String parameterName, 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();
+
+        // 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("if (%s.isBound(\"%s\"))", resourcesFieldName, parameterName);
+        builder.addln("  %s.writeParameter(\"%s\", ($w)$1);", resourcesFieldName, parameterName);
+
+        builder.addln("%s = $1;", fieldName);
+
+        if (cache)
+            builder.addln("%s = %s.isRendering();", cachedFieldName, resourcesFieldName);
+
+        builder.end();
+
+        String methodName = transformation.newMemberName("_update_parameter_" + parameterName);
+
+        MethodSignature signature = new MethodSignature(Modifier.PRIVATE, "void", methodName,
+                new String[]
+                { fieldType }, null);
+
+        transformation.addMethod(signature, 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 invariantFieldName, boolean cache, String parameterName, 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 || ! %s.isLoaded() || ! %<s.isBound(\"%s\")) return %s;",
+                cachedFieldName,
+                resourcesFieldName,
+                parameterName,
+                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.
+
+        builder.addln(
+                "%s result = ($r) ((%s) %s.readParameter(\"%s\", $type));",
+                fieldType,
+                cast,
+                resourcesFieldName,
+                parameterName);
+
+        // 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", invariantFieldName);
+
+        if (cache)
+            builder.add(" || %s.isRendering()", resourcesFieldName);
+
+        builder.addln(")");
+        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_parameter_" + parameterName);
+
+        MethodSignature signature = new MethodSignature(Modifier.PRIVATE, fieldType, methodName,
+                null, null);
+
+        transformation.addMethod(signature, builder.toString());
+
+        transformation.replaceReadAccess(fieldName, methodName);
+    }
+
+    private String getParameterName(String fieldName, String annotatedName)
+    {
+        if (InternalUtils.isNonBlank(annotatedName))
+            return annotatedName;
+
+        return InternalUtils.stripMemberPrefix(fieldName);
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Fri Dec 29 17:08:39 2006
@@ -623,10 +623,6 @@
 
     public void containingPageDidLoad()
     {
-        verifyRequiredParametersAreBound();
-
-        _loaded = true;
-
         // If this component has mixins, add the core component to the end of the list, after the
         // mixins.
 
@@ -661,7 +657,14 @@
             _components = ordered;
         }
 
+        _loaded = true;
+
+        // For some parameters, bindings (from defaults) are provided inside the callback method, so
+        // that is invoked first, before we check for unbound parameters.
+
         invoke(false, CONTAINING_PAGE_DID_LOAD);
+
+        verifyRequiredParametersAreBound();
     }
 
     /**

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java Fri Dec 29 17:08:39 2006
@@ -12,29 +12,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.runtime;
-
-/**
- * A set of methods that allow components to know about page-level operations.
- */
-public interface PageLifecycleListener
-{
-    /**
-     * Invoked when the page finishes loading. This occurs once all components are loaded and all
-     * parameters have been set.
-     */
-    void containingPageDidLoad();
-
-    /**
-     * Invoked when the page is detached, allowing components a chance to clear out any temporary or
-     * client specific state.
-     */
-    void containingPageDidDetach();
-
-    /**
-     * Invoked when a page is first attached to the current request, giving components a chance to
-     * initialize for the current request.
-     */
-
-    void containingPageDidAttach();
-}
+package org.apache.tapestry.runtime;
+
+/**
+ * A set of methods that allow components to know about page-level operations.
+ */
+public interface PageLifecycleListener
+{
+    /**
+     * Invoked when the page finishes loading. This occurs once all components are loaded and all
+     * parameters have been set.
+     */
+    void containingPageDidLoad();
+
+    /**
+     * Invoked when the page is detached, allowing components a chance to clear out any temporary or
+     * client specific state.
+     */
+    void containingPageDidDetach();
+
+    /**
+     * Invoked when a page is first attached to the current request, giving components a chance to
+     * initialize for the current request.
+     */
+
+    void containingPageDidAttach();
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java Fri Dec 29 17:08:39 2006
@@ -29,7 +29,7 @@
      * factory for the default prefix (often "literal") is used (and passed the full prefix).
      * <p>
      * The binding represents a connection between the container and the component (the component is
-     * usually the child of the component, though in a few cases, it is the component itself). In
+     * usually the child of the container, though in a few cases, it is the component itself). In
      * most cases, the expression is evaluated in terms of the resources of the <em>container</em>
      * and the component is ignored.
      * 
@@ -38,7 +38,7 @@
      * @param container
      *            typically, the parent of the component
      * @param component
-     *            the component whose paramter is to be bound
+     *            the component whose parameter is to be bound
      * @param defaultPrefix
      *            the default prefix used when the expression itself does not have a prefix
      * @param expression

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MethodSignature.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MethodSignature.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MethodSignature.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MethodSignature.java Fri Dec 29 17:08:39 2006
@@ -12,231 +12,235 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
-
+package org.apache.tapestry.services;
+
 import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
-
-import java.lang.reflect.Modifier;
-
-/**
- * A representation of a method signature, which consists of its name, modifiers (primarily,
- * visibility), return type, parameter types, and declared exception types.
- * <p>
- * Types are stored as class names (or primitive names) because the MethodSignature is often used in
- * situations where the actual class has not been loaded yet. When classes are already loaded (such
- * as, for use with {@link org.apache.hivemind.service.ClassFab}), then a different implementation
- * with the same name, {@link org.apache.tapestry.ioc.services.MethodSignature} is used.
- */
-public class MethodSignature implements Comparable<MethodSignature>
-{
-    private int _hashCode = -1;
-
-    private final int _modifiers;
-
-    private final String _returnType;
-
-    private final String _methodName;
-
-    private final String[] _parameterTypes;
-
-    private final String[] _exceptionTypes;
-
-    private static final String[] EMPTY_STRINGS = new String[0];
-
-    /** Convienience for adding a public void method with no parameters or exception types. */
-
-    public MethodSignature(String name)
-    {
-        this(Modifier.PUBLIC, "void", name, EMPTY_STRINGS, EMPTY_STRINGS);
-    }
-
-    public MethodSignature(int modifiers, String type, String name, String[] parameterTypes,
-            String[] exceptionTypes)
-    {
-        _modifiers = modifiers;
-
-        _returnType = notBlank(type, "type");
-        _methodName = notBlank(name, "name");
-
-        // TODO: Checks that no element within the two arrays
-        // is null or blank.
-
-        _parameterTypes = typeNamesOrEmpty(parameterTypes);
-        _exceptionTypes = typeNamesOrEmpty(exceptionTypes);
-    }
-
-    private String[] typeNamesOrEmpty(String[] types)
-    {
-        return types == null ? EMPTY_STRINGS : types;
-    }
-
-    /**
-     * Returns a non-null array of the names of each declared exception type thrown by the method.
-     * Calling code should not modify the array.
-     */
-    public String[] getExceptionTypes()
-    {
-        return _exceptionTypes;
-    }
-
-    /** Returns the name of the method. */
-    public String getMethodName()
-    {
-        return _methodName;
-    }
-
-    /**
-     * Returns the set of modifier flags for this method.
-     * 
-     * @see java.lang.reflect.Modifier
-     */
-    public int getModifiers()
-    {
-        return _modifiers;
-    }
-
-    /**
-     * Returns an array of the type name for each parameter. Calling code should not modify the
-     * array.
-     */
-    public String[] getParameterTypes()
-    {
-        return _parameterTypes;
-    }
-
-    /** Return the type name of the return type of the method. */
-    public String getReturnType()
-    {
-        return _returnType;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        if (_hashCode == -1)
-        {
-            _hashCode = 17 * _modifiers;
-            _hashCode += 31 * _returnType.hashCode();
-            _hashCode += 31 * _methodName.hashCode();
-
-            for (String parameterType : _parameterTypes)
-            {
-                _hashCode += 31 * parameterType.hashCode();
-            }
-
-            for (String exceptionType : _exceptionTypes)
-            {
-                _hashCode += 31 * exceptionType.hashCode();
-            }
-        }
-
-        return _hashCode;
-    }
-
-    @Override
-    public boolean equals(Object other)
-    {
-        if (other == null || !(other instanceof MethodSignature))
-            return false;
-
-        MethodSignature ms = (MethodSignature) other;
-
-        return _modifiers == ms._modifiers && _returnType.equals(ms._returnType)
-                && _methodName.equals(ms._methodName)
-                && matches(_parameterTypes, ms._parameterTypes)
-                && matches(_exceptionTypes, ms._exceptionTypes);
-    }
-
-    private boolean matches(String[] values, String[] otherValues)
-    {
-        if (values.length != otherValues.length)
-            return false;
-
-        for (int i = 0; i < values.length; i++)
-        {
-            if (!values[i].equals(otherValues[i]))
-                return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns the long form description of the signature. This includes modifiers, return type,
-     * method name, parameters and thrown exceptions, formatter approximately as it would appear in
-     * Java source (except that parameter names, which are not known, do no appear).
-     */
-    @Override
-    public String toString()
-    {
-        StringBuilder builder = new StringBuilder();
-
-        // Package private is simply omitted.
-
-        if (_modifiers != 0)
-        {
-            builder.append(Modifier.toString(_modifiers));
-            builder.append(' ');
-        }
-
-        builder.append(_returnType);
-        builder.append(' ');
-
-        addMethodNameAndParameters(builder);
-
-        for (int i = 0; i < _exceptionTypes.length; i++)
-        {
-            if (i == 0)
-                builder.append(" throws ");
-            else
-                builder.append(", ");
-
-            builder.append(_exceptionTypes[i]);
-        }
-
-        return builder.toString();
-    }
-
-    private void addMethodNameAndParameters(StringBuilder builder)
-    {
-        builder.append(_methodName);
-        builder.append('(');
-
-        for (int i = 0; i < _parameterTypes.length; i++)
-        {
-            if (i > 0)
-                builder.append(", ");
-
-            builder.append(_parameterTypes[i]);
-        }
-
-        builder.append(')');
-    }
-
-    public int compareTo(MethodSignature o)
-    {
-        int result = _methodName.compareTo(o._methodName);
-
-        if (result == 0)
-            result = _parameterTypes.length - o._parameterTypes.length;
-
-        return result;
-    }
-
-    /**
-     * Returns a shortened form of the string representation of the method. It lists just the name
-     * of the method and the types of any parameters, omitting return type, exceptions and
-     * modifiers.
-     * 
-     * @return
-     */
-    public String getMediumDescription()
-    {
-        StringBuilder builder = new StringBuilder();
-
-        addMethodNameAndParameters(builder);
-
-        return builder.toString();
-    }
-
-}
+
+import java.lang.reflect.Modifier;
+
+/**
+ * A representation of a method signature, which consists of its name, modifiers (primarily,
+ * visibility), return type, parameter types, and declared exception types.
+ * <p>
+ * Types are stored as class names (or primitive names) because the MethodSignature is often used in
+ * situations where the actual class has not been loaded yet. When classes are already loaded (such
+ * as, for use with {@link org.apache.hivemind.service.ClassFab}), then a different implementation
+ * with the same name, {@link org.apache.tapestry.ioc.services.MethodSignature} is used.
+ */
+public class MethodSignature implements Comparable<MethodSignature>
+{
+    private int _hashCode = -1;
+
+    private final int _modifiers;
+
+    private final String _returnType;
+
+    private final String _methodName;
+
+    private final String[] _parameterTypes;
+
+    private final String[] _exceptionTypes;
+
+    private static final String[] EMPTY_STRINGS = new String[0];
+
+    /** Convienience for adding a public void method with no parameters or exception types. */
+
+    public MethodSignature(String name)
+    {
+        this(Modifier.PUBLIC, "void", name, EMPTY_STRINGS, EMPTY_STRINGS);
+    }
+
+    public MethodSignature(int modifiers, String type, String name, String[] parameterTypes,
+            String[] exceptionTypes)
+    {
+        _modifiers = modifiers;
+
+        _returnType = notBlank(type, "type");
+        _methodName = notBlank(name, "name");
+
+        // TODO: Checks that no element within the two arrays
+        // is null or blank.
+
+        _parameterTypes = typeNamesOrEmpty(parameterTypes);
+        _exceptionTypes = typeNamesOrEmpty(exceptionTypes);
+    }
+
+    private String[] typeNamesOrEmpty(String[] types)
+    {
+        return types == null ? EMPTY_STRINGS : types;
+    }
+
+    /**
+     * Returns a non-null array of the names of each declared exception type thrown by the method.
+     * Calling code should not modify the array.
+     */
+    public String[] getExceptionTypes()
+    {
+        return _exceptionTypes;
+    }
+
+    /** Returns the name of the method. */
+    public String getMethodName()
+    {
+        return _methodName;
+    }
+
+    /**
+     * Returns the set of modifier flags for this method.
+     * 
+     * @see java.lang.reflect.Modifier
+     */
+    public int getModifiers()
+    {
+        return _modifiers;
+    }
+
+    /**
+     * Returns an array of the type name for each parameter. Calling code should not modify the
+     * array.
+     */
+    public String[] getParameterTypes()
+    {
+        return _parameterTypes;
+    }
+
+    /** Return the type name of the return type of the method. */
+    public String getReturnType()
+    {
+        return _returnType;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        if (_hashCode == -1)
+        {
+            _hashCode = 17 * _modifiers;
+            _hashCode += 31 * _returnType.hashCode();
+            _hashCode += 31 * _methodName.hashCode();
+
+            for (String parameterType : _parameterTypes)
+            {
+                _hashCode += 31 * parameterType.hashCode();
+            }
+
+            for (String exceptionType : _exceptionTypes)
+            {
+                _hashCode += 31 * exceptionType.hashCode();
+            }
+        }
+
+        return _hashCode;
+    }
+
+    @Override
+    public boolean equals(Object other)
+    {
+        if (other == null || !(other instanceof MethodSignature))
+            return false;
+
+        MethodSignature ms = (MethodSignature) other;
+
+        return _modifiers == ms._modifiers && _returnType.equals(ms._returnType)
+                && _methodName.equals(ms._methodName)
+                && matches(_parameterTypes, ms._parameterTypes)
+                && matches(_exceptionTypes, ms._exceptionTypes);
+    }
+
+    private boolean matches(String[] values, String[] otherValues)
+    {
+        if (values.length != otherValues.length)
+            return false;
+
+        for (int i = 0; i < values.length; i++)
+        {
+            if (!values[i].equals(otherValues[i]))
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the long form description of the signature. This includes modifiers, return type,
+     * method name, parameters and thrown exceptions, formatter approximately as it would appear in
+     * Java source (except that parameter names, which are not known, do no appear).
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder builder = new StringBuilder();
+
+        // Package private is simply omitted.
+
+        if (_modifiers != 0)
+        {
+            builder.append(Modifier.toString(_modifiers));
+            builder.append(' ');
+        }
+
+        builder.append(_returnType);
+        builder.append(' ');
+
+        addMethodNameAndParameters(builder);
+
+        for (int i = 0; i < _exceptionTypes.length; i++)
+        {
+            if (i == 0)
+                builder.append(" throws ");
+            else
+                builder.append(", ");
+
+            builder.append(_exceptionTypes[i]);
+        }
+
+        return builder.toString();
+    }
+
+    private void addMethodNameAndParameters(StringBuilder builder)
+    {
+        builder.append(_methodName);
+        builder.append('(');
+
+        for (int i = 0; i < _parameterTypes.length; i++)
+        {
+            if (i > 0)
+                builder.append(", ");
+
+            builder.append(_parameterTypes[i]);
+        }
+
+        builder.append(')');
+    }
+
+    /**
+     * Sorting is primarily via method name. For methods with the same name, the second level of
+     * sorting is by parameter count (ascending).
+     */
+    public int compareTo(MethodSignature o)
+    {
+        int result = _methodName.compareTo(o._methodName);
+
+        if (result == 0)
+            result = _parameterTypes.length - o._parameterTypes.length;
+
+        return result;
+    }
+
+    /**
+     * Returns a shortened form of the string representation of the method. It lists just the name
+     * of the method and the types of any parameters, omitting return type, exceptions and
+     * modifiers.
+     * 
+     * @return
+     */
+    public String getMediumDescription()
+    {
+        StringBuilder builder = new StringBuilder();
+
+        addMethodNameAndParameters(builder);
+
+        return builder.toString();
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Fri Dec 29 17:08:39 2006
@@ -53,11 +53,11 @@
 import org.apache.tapestry.internal.services.BindingSourceImpl;
 import org.apache.tapestry.internal.services.ClasspathAssetAliasManagerImpl;
 import org.apache.tapestry.internal.services.CommonResourcesInjectionProvider;
-import org.apache.tapestry.internal.services.ComponentClassFactoryImpl;
 import org.apache.tapestry.internal.services.ComponentClassResolverImpl;
 import org.apache.tapestry.internal.services.ComponentEventDispatcher;
 import org.apache.tapestry.internal.services.ComponentInstanceEventHandler;
 import org.apache.tapestry.internal.services.ComponentInstantiatorSource;
+import org.apache.tapestry.internal.services.ComponentInvocationMap;
 import org.apache.tapestry.internal.services.ComponentLifecycleMethodWorker;
 import org.apache.tapestry.internal.services.ComponentMessagesSourceImpl;
 import org.apache.tapestry.internal.services.ComponentResourcesInjectionProvider;
@@ -69,7 +69,6 @@
 import org.apache.tapestry.internal.services.EnvironmentalWorker;
 import org.apache.tapestry.internal.services.FieldValidatorSourceImpl;
 import org.apache.tapestry.internal.services.HeartbeatImpl;
-import org.apache.tapestry.internal.services.ComponentInvocationMap;
 import org.apache.tapestry.internal.services.InfrastructureImpl;
 import org.apache.tapestry.internal.services.InfrastructureManagerImpl;
 import org.apache.tapestry.internal.services.InjectAnonymousWorker;
@@ -538,11 +537,12 @@
      * Returns a {@link ClassFactory} that can be used to create extra classes around component
      * classes.
      */
-    public static ClassFactory buildComponentClassFactory(Log log,
+    public ClassFactory buildComponentClassFactory(
             @InjectService("tapestry.internal.ComponentInstantiatorSource")
             ComponentInstantiatorSource source)
     {
-        return new ComponentClassFactoryImpl(log, source);
+
+        return _shadowBuilder.build(source, "classFactory", ClassFactory.class);
     }
 
     /**
@@ -603,7 +603,8 @@
             Environment environment, @InjectService("tapestry.ComponentClassResolver")
             ComponentClassResolver resolver, @InjectService("tapestry.internal.RequestPageCache")
             RequestPageCache requestPageCache, @Inject("infrastructure:AssetSource")
-            AssetSource assetSource)
+            AssetSource assetSource, @Inject("infrastructure:BindingSource")
+            BindingSource bindingsource)
     {
         // TODO: Proper scheduling of all of this. Since a given field or method should
         // only have a single annotation, the order doesn't matter so much, as long as
@@ -617,7 +618,7 @@
         configuration.add("AssetInject", new AssetInjectWorker(assetSource), "before:InjectNamed");
         configuration.add("MixinAfter", new MixinAfterWorker());
 
-        configuration.add("Parameter", new ParameterWorker());
+        configuration.add("Parameter", new ParameterWorker(bindingsource));
         configuration.add("Component", new ComponentWorker(resolver));
         configuration.add("Environment", new EnvironmentalWorker(environment));
         configuration.add("Mixin", new MixinWorker(resolver));

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt Fri Dec 29 17:08:39 2006
@@ -225,6 +225,29 @@
   the min parameter has a default value of 1. That value is used unless the min parameter is bound,
   in which case, the bound value supercedes the default.
   
+Parameter Binding Defaults
+
+  The Parameter annotation's value() attribute can be used to specify a <binding expression> that will be the
+  default binding for the parameter is otherwise left unbound. Typically, this is the name of a property
+  that that will compute the value on the fly.  
+  
+  Example:
+  
++----+
+  @Parameter("defaultMessage")
+  private String _message;
+  
+  @Parameter(required=true)
+  private int _maxLength;
+  
+  public String getDefaultMessage()
+  {
+    return String.format("Maximum field length is %d.", _maxLength);
+  }
++---+
+
+  As elsewhere, you may use a prefix on the value. A common prefix to use is the "message:" prefix, to access a localized message.  
+  
 Unbound Parameters
 
   If a parameter is not bound (and is optional), then the value may be read or <updated> at any time.
@@ -232,11 +255,12 @@
   Updates to unbound parameters cause no side effects.  In the first example, the value parameter of the Count
   component is not bound, and this is perfectly valid.
   
-  Note: updates to such fields are temporary; when the component finishes rendering, the field
+  Note: updates to such fields are temporary; when the component <finishes rendering>, the field
   will revert to its default value.
   
   <<TODO: This seems contradictory. What does it mean to update an unbound component parameter when the component 
-  is not rendering?>>
+  is not rendering?>>  
+    
   
 Parameter Caching
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html Fri Dec 29 17:08:39 2006
@@ -57,6 +57,9 @@
             <li>
                 <a href="InjectComponentMismatch.html">InjectComponentMismatch</a> -- check error reporting when @InjectComponent doesn't match the actual field type
             </li>
+            <li>
+                <a href="ParameterDefault.html">ParameterDefault</a> -- defaulter methods for component parameters
+            </li>
             </ul>
         </p>
     </body>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Fri Dec 29 17:08:39 2006
@@ -374,6 +374,15 @@
         assertEquals(downloaded, actual);
     }
 
+    @Test
+    public void component_parameter_default_from_method() throws Exception
+    {
+        _selenium.open(BASE_URL);
+        clickAndWait("link=ParameterDefault");
+
+        assertTextPresent("Echo component default: [org.apache.tapestry.integration.app1.pages.ParameterDefault:echo]");
+    }
+
     /**
      * {@link InjectComponentWorker} is largely tested by the forms tests ({@link RenderDisabled}
      * is built on it). This test is for the failure case, where a mixin class is used with the

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Echo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Echo.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Echo.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/Echo.java Fri Dec 29 17:08:39 2006
@@ -12,26 +12,26 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.integration.app1.components;
-
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.annotations.BeginRender;
-import org.apache.tapestry.annotations.ComponentClass;
-import org.apache.tapestry.annotations.Parameter;
-
-/**
- * Echos out its value parameter (used to test parameter overrides between component annoation and
- * template).
- */
-@ComponentClass
-public class Echo
-{
-    @Parameter(required = true)
-    private String _value;
-
-    @BeginRender
-    void render(MarkupWriter writer)
-    {
-        writer.write(_value);
-    }
-}
+package org.apache.tapestry.integration.app1.components;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.annotations.BeginRender;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Parameter;
+
+/**
+ * Echos out its value parameter. Uused to test parameter overrides between component annoation and
+ * template. Also, used to test parameter defaulter methods.
+ */
+@ComponentClass
+public class Echo
+{
+    @Parameter("componentResources.completeId")
+    private String _value;
+
+    @BeginRender
+    void render(MarkupWriter writer)
+    {
+        writer.write(_value);
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ParameterDefault.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ParameterDefault.java?view=auto&rev=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ParameterDefault.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ParameterDefault.java Fri Dec 29 17:08:39 2006
@@ -0,0 +1,23 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.ComponentClass;
+
+@ComponentClass
+public class ParameterDefault
+{
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java Fri Dec 29 17:08:39 2006
@@ -74,18 +74,13 @@
 
     public void afterRenderBody(MarkupWriter writer, LifecycleEvent<Boolean> event)
     {
-        // TODO Auto-generated method stub
-
     }
 
     public void afterRenderTemplate(MarkupWriter writer, LifecycleEvent<Boolean> event)
     {
-        // TODO Auto-generated method stub
-
     }
 
     public void beforeRenderTemplate(MarkupWriter writer, LifecycleEvent<Boolean> event)
     {
-        // TODO Auto-generated method stub
     }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/DefaultParameterComponent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/DefaultParameterComponent.java?view=auto&rev=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/DefaultParameterComponent.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/DefaultParameterComponent.java Fri Dec 29 17:08:39 2006
@@ -0,0 +1,34 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Parameter;
+
+/**
+ * Used by {@link org.apache.tapestry.internal.services.ParameterWorkerTest}.
+ */
+@ComponentClass
+public class DefaultParameterComponent
+{
+    @Parameter("literal:greeting")
+    private String _value;
+
+    public String getValue()
+    {
+        return _value;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLocatorTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLocatorTest.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLocatorTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLocatorTest.java Fri Dec 29 17:08:39 2006
@@ -1,3 +1,17 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package org.apache.tapestry.internal.services;
 
 import java.util.Locale;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ParameterWorkerTest.java Fri Dec 29 17:08:39 2006
@@ -21,11 +21,14 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.InternalComponentResources;
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.ioc.internal.services.PropertyAccessImpl;
 import org.apache.tapestry.ioc.services.PropertyAccess;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.Binding;
+import org.apache.tapestry.services.BindingSource;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
@@ -450,6 +453,59 @@
         verify();
     }
 
+    @Test
+    public void parameter_with_default() throws Exception
+    {
+        final BindingSource source = newBindingSource();
+        final InternalComponentResources resources = newInternalComponentResources();
+        final Binding binding = newBinding();
+        String boundValue = "howdy!";
+
+        MutableComponentModel model = newMutableComponentModel();
+
+        model.addParameter("value", false);
+
+        Runnable phaseTwoTraining = new Runnable()
+        {
+            public void run()
+            {
+                train_isBound(resources, "value", false);
+
+                expect(
+                        source.newBinding(
+                                "default value",
+                                resources,
+                                resources,
+                                InternalConstants.PROP_BINDING_PREFIX,
+                                "literal:greeting",
+                                null)).andReturn(binding);
+
+                resources.addParameter("value", binding);
+
+                train_isInvariant(resources, "value", true);
+
+            };
+        };
+
+        Component component = setupForIntegrationTest(
+                resources,
+                newLog(),
+                DefaultParameterComponent.class.getName(),
+                model,
+                source,
+                phaseTwoTraining);
+
+        train_isLoaded(resources, true);
+        train_isBound(resources, "value", true);
+        train_readParameter(resources, "value", String.class, boundValue);
+
+        replay();
+
+        assertEquals(_access.get(component, "value"), boundValue);
+
+        verify();
+    }
+
     protected final void train_isRendering(InternalComponentResources resources, boolean rendering)
     {
         expect(resources.isRendering()).andReturn(rendering);
@@ -461,10 +517,40 @@
         expect(resources.readParameter(parameterName, expectedType)).andReturn(value);
     }
 
-    private Component setupForIntegrationTest(InternalComponentResources resources)
+    /** This is for the majority of tests. */
+    private Component setupForIntegrationTest(final InternalComponentResources resources)
             throws Exception
     {
-        Log log = newLog();
+        MutableComponentModel model = newMutableComponentModel();
+
+        model.addParameter("invariantObject", false);
+        model.addParameter("invariantPrimitive", false);
+        model.addParameter("object", false);
+        model.addParameter("primitive", true);
+        model.addParameter("uncached", false);
+
+        Runnable phaseTwoTraining = new Runnable()
+        {
+            public void run()
+            {
+                trainForPageDidLoad(resources);
+
+            }
+        };
+
+        return setupForIntegrationTest(
+                resources,
+                newLog(),
+                ParameterComponent.class.getName(),
+                model,
+                newBindingSource(),
+                phaseTwoTraining);
+    }
+
+    private Component setupForIntegrationTest(InternalComponentResources resources, Log log,
+            String componentClassName, MutableComponentModel model, BindingSource source,
+            Runnable phaseTwoTraining) throws Exception
+    {
         ClassPool pool = new ClassPool();
         ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
         pool.appendClassPath(new LoaderClassPath(contextLoader));
@@ -473,21 +559,13 @@
 
         loader.delegateLoadingOf("org.apache.tapestry.");
 
-        CtClass ctClass = pool.get(ParameterComponent.class.getName());
+        CtClass ctClass = pool.get(componentClassName);
         InternalClassTransformation transformation = new InternalClassTransformationImpl(ctClass,
                 _contextClassLoader, log, null);
 
-        MutableComponentModel model = newMutableComponentModel();
-
-        model.addParameter("invariantObject", false);
-        model.addParameter("invariantPrimitive", false);
-        model.addParameter("object", false);
-        model.addParameter("primitive", true);
-        model.addParameter("uncached", false);
-
         replay();
 
-        new ParameterWorker().transform(transformation, model);
+        new ParameterWorker(source).transform(transformation, model);
 
         verify();
 
@@ -499,7 +577,7 @@
 
         Instantiator instantiator = transformation.createInstantiator(transformedClass);
 
-        trainForPageDidLoad(resources);
+        phaseTwoTraining.run();
 
         replay();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java?view=diff&rev=491145&r1=491144&r2=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java Fri Dec 29 17:08:39 2006
@@ -150,6 +150,10 @@
         train_getParameterModel(model, "fred", pmodel);
         train_isRequired(pmodel, true);
 
+        // Now called *before* the check for unbound parametesr
+
+        component.containingPageDidLoad();
+
         train_getComponentClassName(model, "foo.components.MyComponent");
 
         replay();

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterDefault.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterDefault.html?view=auto&rev=491145
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterDefault.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ParameterDefault.html Fri Dec 29 17:08:39 2006
@@ -0,0 +1,6 @@
+<t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    
+
+  Echo component default: [<t:comp type="Echo" id="echo"/>]
+    
+</t:comp>