You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/01/31 21:18:52 UTC

svn commit: r501975 [1/2] - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/beaneditor/ main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/internal/beaneditor/ ma...

Author: hlship
Date: Wed Jan 31 12:18:49 2007
New Revision: 501975

URL: http://svn.apache.org/viewvc?view=rev&rev=501975
Log:
Add initial pass at BeanEditor component and surrounding infrastructure.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/BeanEditorModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Order.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyConduit.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyEditModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Validate.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Delegate.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorModelImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PrimitiveFieldConstraintGenerator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PropertyEditModelImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/ValidateAnnotationConstraintGenerator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BeanEditorModelSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CompositeFieldValidator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidationConstraintGeneratorImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/NotificationEventHandler.java
      - copied, changed from r499593, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/AcceptVoidEventHandler.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BeanEditorModelSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ValidationConstraintGenerator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/corelib/components/BeanEditor.html
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/beaneditor/
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/beaneditor/BeanEditorStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/BeanEditorDemo.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/WEB-INF/ViewRegistration.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/data/RegistrationData.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/BeanEditorDemo.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ViewRegistration.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/beaneditor/
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/beaneditor/ValidateAnnotationConstraintGeneratorTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/BeanEditorModelSourceImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/SimpleBean.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/StoogeBean.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/util/NotificationEventHandlerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/BeanEditorDemo.properties
Removed:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/AcceptVoidEventHandler.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.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/services/FieldValidatorSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Max.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MaxLength.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Min.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Required.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/InternalStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml
    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/internal/services/FieldValidatorImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/FieldValidatorSourceImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ValidForm.properties

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java Wed Jan 31 12:18:49 2007
@@ -89,7 +89,8 @@
      *            parameters (may be null)
      * @param handler
      *            the handler to be informed of the result, or null if the event is a notification
-     *            that does not support return values from event handler methods
+     *            that does not support return values from event handler methods (the value true is
+     *            allowed even if the handler is null).
      * @return true if any event handler was invoked (even if no event handler method returns a
      *         non-null value)
      * @see OnEventWorker

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java Wed Jan 31 12:18:49 2007
@@ -70,8 +70,8 @@
             throws ValidationException;
 
     /**
-     * Returns true if the validator should be skipped for null or blank (empty string) values. This
-     * is generally true.
+     * Returns true if the validator should be invoked for null or blank (empty string) values. This
+     * is generally false.
      */
-    boolean skipIfBlank();
+    boolean invokeIfBlank();
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/BeanEditorModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/BeanEditorModel.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/BeanEditorModel.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/BeanEditorModel.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,55 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.beaneditor;
+
+import java.util.List;
+
+/**
+ * Provides the information necessary to build a user interface to create or edit an instance of a
+ * particular type.
+ * <p>
+ * BeanEditorModels are not threadsafe, they are also not serializable.
+ */
+public interface BeanEditorModel
+{
+    /**
+     * Returns a list of the editable properties of the bean, in <em>presentation</em> order.
+     * 
+     * @return
+     */
+    List<String> getPropertyNames();
+
+    /**
+     * Returns the named model.
+     * 
+     * @param propertyName
+     *            name of property to retrieve model for
+     * @return the model for the property
+     * @throws RuntimeException
+     *             if the bean editor model does not have a property model for the provided name
+     */
+    PropertyEditModel get(String propertyName);
+
+    /**
+     * Adds a new property to the model, returning its mutable model for further refinement.
+     * 
+     * @param propertyName
+     *            name of property to add
+     * @return the model for the property
+     * @throws RuntimeException
+     *             if the property already exists
+     */
+    PropertyEditModel add(String propertyName);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Order.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Order.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Order.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Order.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,36 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.beaneditor;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Placed on either a property getter or a property setter method to control the order in which the
+ * properties are presented to the user. The properties will be sorted in ascending order by the
+ * value. Properties with no value will be treated as though they have the value 0. When multiple
+ * properties have the same order value, they will be sorted alphabetically.
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+@Documented
+public @interface Order {
+    /** The sort order for this property. */
+    int value();
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyConduit.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyConduit.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyConduit.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyConduit.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,53 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.beaneditor;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Used to read or update the value associated with a property.
+ */
+public interface PropertyConduit
+{
+    /**
+     * Reads the property from the instance.
+     * 
+     * @param instance
+     *            object containing the property
+     * @return the current value of the property
+     */
+    Object read(Object instance);
+
+    /**
+     * Changes the current value of the property.
+     * 
+     * @param instance
+     *            object containing the property
+     * @param value
+     *            to change the property to
+     */
+    void write(Object instance, Object value);
+
+    /**
+     * Returns an annotation if present on the property. The annotation is checked for on both the
+     * getter method and the setter method.
+     * 
+     * @param <T>
+     * @param annotationClass
+     *            the type of annotation to return
+     * @return the annotation if present, or null if not available
+     */
+    <T extends Annotation> T getAnnotation(Class<T> annotationClass);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyEditModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyEditModel.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyEditModel.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/PropertyEditModel.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,89 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.beaneditor;
+
+/**
+ * Part of a {@link BeanEditorModel} that defines the attributes of a single edittable property of a
+ * bean.
+ */
+public interface PropertyEditModel
+{
+    /** Returns the name of the property. */
+    String getPropertyName();
+
+    /** Returns a user-presentable label for the property. */
+    String getLabel();
+
+    /** Returns the order vlaue, used to sort the properties into presentation order. */
+    int getOrder();
+
+    /** Returns the type of the property. */
+    Class getPropertyType();
+
+    /**
+     * Returns an object used to read or update the property. For virtual properties (properties
+     * that do not actually exist on the bean), the conduit may be null until one is provided via
+     * {@link MutablePropertyEditModel#conduit(PropertyConduit)}.
+     */
+    PropertyConduit getConduit();
+
+    /**
+     * Changes the property type for the property.
+     * 
+     * @param propertyType
+     *            the new property type for the property.
+     * @return the property's model, for further changes
+     */
+    PropertyEditModel propertyType(Class propertyType);
+
+    /**
+     * Changes the label for the property to the provided value.
+     * 
+     * @param label
+     *            new label for property
+     * @return the property's model, for further changes
+     */
+    PropertyEditModel label(String label);
+
+    /**
+     * Changes the order for the property. The properties are sorted by order (and then by name for
+     * identical orders) when building the user interface.
+     */
+    PropertyEditModel order(int order);
+
+    /**
+     * Changes the conduit for the property to the provided value.
+     * 
+     * @param conduit
+     *            new conduit object (used to read or update the property value on an instance)
+     * @return the property's model, for further changes
+     */
+    PropertyEditModel conduit(PropertyConduit conduit);
+
+    // "Navigation" methods useful for the "fluent" style of configuration.
+
+    /**
+     * Delegates to {@link BeanEditorModel#get(String)}.
+     */
+    PropertyEditModel get(String propertyName);
+
+    /**
+     * Delegates to {@link BeanEditorModel#add(String)}.
+     */
+    PropertyEditModel add(String propertyName);
+
+    /** Returns the containing model. */
+    BeanEditorModel model();
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Validate.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Validate.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Validate.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/beaneditor/Validate.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,36 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.beaneditor;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Used to attach validation constraints directly to a property (either the getter or the setter
+ * method). The annotation value is a comma seperated list of <em>validation constraints</em>,
+ * each one identifying a validator type (such as "required", "minlength") and optionally, a
+ * constraint value. Most validators need a constraint value, which is seperated from the type by an
+ * equals size (i.e., "maxlength=30").
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+@Documented
+public @interface Validate {
+    String value();
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,196 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.components;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.tapestry.Block;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Field;
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.Translator;
+import org.apache.tapestry.annotations.Component;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Inject;
+import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.beaneditor.BeanEditorModel;
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.internal.services.CompositeFieldValidator;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.services.BeanEditorModelSource;
+import org.apache.tapestry.services.FieldValidatorSource;
+import org.apache.tapestry.services.TranslatorDefaultSource;
+import org.apache.tapestry.services.ValidationConstraintGenerator;
+
+/**
+ * A component that creates an entire form editting the properties of a particular bean. Inspired by
+ * <a href="http://www.trailsframework.org/">Trails</a> and <a
+ * href="http://beanform.sourceforge.net/">BeanForm</a> (both for Tapestry 4). Generates a simple
+ * UI for editting the properties of a JavaBean, with the flavor of UI for each property (text
+ * field, checkbox, drop down list) determined from the property type, and the order and validation
+ * for the properties determined from annotations on the property's getter and setter methods.
+ * <p>
+ * This component is likely to change more than any other thing in Tapestry! What's available now is
+ * a very limited preview of its eventual functionality.
+ * 
+ * @see BeanEditorModel
+ * @see BeanEditorModelSource
+ */
+@ComponentClass
+public class BeanEditor
+{
+    /** The object to be editted by the BeanEditor. */
+    @Parameter(required = true)
+    private Object _object;
+
+    @Inject
+    private ComponentResources _resources;
+
+    @Inject("infrastructure:BeanEditorModelSource")
+    private BeanEditorModelSource _modelSource;
+
+    @Inject("infrastructure:TranslatorDefaultSource")
+    private TranslatorDefaultSource _translatorDefaultSource;
+
+    @Inject("infrastructure:FieldValidatorSource")
+    private FieldValidatorSource _fieldValidatorSource;
+
+    @Inject("infrastructure:ValidationConstraintGenerator")
+    private ValidationConstraintGenerator _validationConstraintGenerator;
+
+    @Inject
+    private Block _text;
+
+    @Component(parameters =
+    { "value=valueForProperty", "label=prop:propertyEditModel.label",
+            "translate=prop:translateForProperty", "validate=prop:validateForProperty" })
+    private TextField _textField;
+
+    @Inject
+    private Locale _locale;
+
+    @Parameter
+    private BeanEditorModel _model;
+
+    // Values that change with each change to the current property:
+
+    private PropertyEditModel _propertyEditModel;
+
+    private Block _blockForProperty;
+
+    private Field _fieldForProperty;
+
+    public BeanEditorModel getModel()
+    {
+        return _model;
+    }
+
+    public void setPropertyName(String propertyName)
+    {
+        _propertyEditModel = _model.get(propertyName);
+
+        // Shortly, we'll do a bit more work to set these two values.
+
+        _blockForProperty = _text;
+        _fieldForProperty = _textField;
+    }
+
+    public String getPropertyName()
+    {
+        return _propertyEditModel.getPropertyName();
+    }
+
+    boolean onPrepareFromForm()
+    {
+        // Fire a new prepare event to be consumed by the container. This is the container's
+        // chance to ensure that there's an object to edit.
+
+        _resources.triggerEvent(Form.PREPARE, null, null);
+
+        if (_model == null)
+        {
+            Class beanType = _object.getClass();
+
+            _model = _modelSource.create(beanType, _resources.getContainerResources());
+        }
+
+        return true; // abort the form's prepare event
+    }
+
+    public Translator getTranslateForProperty()
+    {
+        return _translatorDefaultSource.find(_propertyEditModel.getPropertyType());
+    }
+
+    public FieldValidator getValidateForProperty()
+    {
+        List<FieldValidator> validators = newList();
+
+        // Use the property name, not the field id, when locating
+        // validation message overrides.
+        String overrideId = _propertyEditModel.getPropertyName();
+        // Pull override messages out of the BeanEditor's container
+        Messages overrideMessages = _resources.getContainerResources().getMessages();
+
+        for (String constraint : _validationConstraintGenerator
+                .buildConstraints(_propertyEditModel))
+        {
+            int equalsx = constraint.indexOf('=');
+
+            String validatorType = equalsx > 0 ? constraint.substring(0, equalsx) : constraint;
+            String constraintValue = equalsx > 0 ? constraint.substring(equalsx + 1) : null;
+
+            FieldValidator validator = _fieldValidatorSource.createValidator(
+                    _fieldForProperty,
+                    validatorType,
+                    constraintValue,
+                    overrideId,
+                    overrideMessages,
+                    _locale);
+
+            validators.add(validator);
+        }
+
+        return new CompositeFieldValidator(validators);
+    }
+
+    public PropertyEditModel getPropertyEditModel()
+    {
+        return _propertyEditModel;
+    }
+
+    public Block getBlockForProperty()
+    {
+        return _blockForProperty;
+    }
+
+    public Field getFieldForProperty()
+    {
+        return _fieldForProperty;
+    }
+
+    public Object getValueForProperty()
+    {
+        return _propertyEditModel.getConduit().read(_object);
+    }
+
+    public void setValueForProperty(Object value)
+    {
+        _propertyEditModel.getConduit().write(_object, value);
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Delegate.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Delegate.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Delegate.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Delegate.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,36 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.components;
+
+import org.apache.tapestry.Block;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Parameter;
+
+/**
+ * A component that does not do any rendering of its own, but will delegate to some other object
+ * that can do rendering. This other object may be a component or a {@link Block} (among other
+ * things).
+ */
+@ComponentClass
+public class Delegate
+{
+    @Parameter(required = true)
+    private Object _to;
+
+    Object beginRender()
+    {
+        return _to;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java Wed Jan 31 12:18:49 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -58,9 +58,9 @@
 /**
  * An HTML form, which will enclose other components to render out the various types of fields.
  * <p>
- * A Form emits several notification events; when it renders it sends a
- * {@link #PREPARE_EVENT prepare} notification event, to allow any listeners to set up the state of
- * the page prior to rendering out the form's content.
+ * A Form emits several notification events; when it renders it sends a {@link #PREPARE prepare}
+ * notification event, to allow any listeners to set up the state of the page prior to rendering out
+ * the form's content.
  * <p>
  * When the form is submitted, the component emits four notifications: first another prepare event
  * to allow the page to update its state as necessary to prepare for the form submission, then
@@ -80,7 +80,7 @@
      * Invoked to let the containing component(s) prepare for the form rendering or the form
      * submission.
      */
-    public static final String PREPARE_EVENT = "prepare";
+    public static final String PREPARE = "prepare";
 
     /**
      * Event type for a notification after the form has submitted. This event notification occurs on
@@ -199,7 +199,7 @@
 
         Object[] contextArray = _context == null ? new Object[0] : _context.toArray();
 
-        _resources.triggerEvent(PREPARE_EVENT, contextArray, null);
+        _resources.triggerEvent(PREPARE, contextArray, null);
 
         String name = _pageRenderSupport.allocateClientId(_resources.getId());
 
@@ -289,6 +289,9 @@
                 public boolean handleResult(Object result, Component component,
                         String methodDescription)
                 {
+                    if (result instanceof Boolean)
+                        return ((Boolean) result);
+
                     holder.put(_eventResultProcessor.processComponentEvent(
                             result,
                             component,
@@ -298,7 +301,7 @@
                 }
             };
 
-            _resources.triggerEvent(PREPARE_EVENT, context, handler);
+            _resources.triggerEvent(PREPARE, context, handler);
 
             if (holder.hasValue())
                 return holder.get();
@@ -355,7 +358,7 @@
             // If the tracker has no errors, then clear it of any input values
             // as well, so that the next page render will be "clean" and show
             // true persistent data, not value from the previous form submission.
-            
+
             if (!_tracker.getHasErrors())
                 _tracker.clear();
 
@@ -379,7 +382,7 @@
     }
 
     /**
-     * A convienience for invoking {@link ValidationTracker#recordError(String)}. 
+     * A convienience for invoking {@link ValidationTracker#recordError(String)}.
      */
     public void recordError(String errorMessage)
     {

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorMessages.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorMessages.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorMessages.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,38 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.beaneditor;
+
+import java.util.Collection;
+
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.internal.util.InternalUtils;
+import org.apache.tapestry.ioc.internal.util.MessagesImpl;
+
+class BeanEditorMessages
+{
+    private static final Messages MESSAGES = MessagesImpl.forClass(BeanEditorMessages.class);
+
+    static String duplicatePropertyName(Class beanType, String propertyName)
+    {
+        return MESSAGES.format("duplicate-property-name", beanType.getName(), propertyName);
+    }
+
+    static String unknownProperty(Class beanType, String propertyName,
+            Collection<String> propertyNames)
+    {
+        return MESSAGES.format("unknown-property", beanType.getName(), propertyName, InternalUtils
+                .joinSorted(propertyNames));
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorModelImpl.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorModelImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/BeanEditorModelImpl.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,179 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.beaneditor;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry.beaneditor.BeanEditorModel;
+import org.apache.tapestry.beaneditor.PropertyConduit;
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.internal.TapestryUtils;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.services.ClassPropertyAdapter;
+import org.apache.tapestry.ioc.services.PropertyAdapter;
+import org.apache.tapestry.ioc.services.TypeCoercer;
+
+public class BeanEditorModelImpl implements BeanEditorModel, Comparator<PropertyEditModel>
+{
+    private final Class _beanType;
+
+    private final ClassPropertyAdapter _classPropertyAdapter;
+
+    private final TypeCoercer _typeCoercer;
+
+    private final Messages _messages;
+
+    private final Map<String, PropertyEditModel> _properties = newMap();
+
+    public BeanEditorModelImpl(Class beanType, ClassPropertyAdapter classPropertyAdapter,
+            TypeCoercer typeCoercer, Messages messages)
+    {
+        _beanType = beanType;
+        _classPropertyAdapter = classPropertyAdapter;
+        _typeCoercer = typeCoercer;
+        _messages = messages;
+    }
+
+    public PropertyEditModel add(String propertyName)
+    {
+        if (_properties.containsKey(propertyName))
+            throw new RuntimeException(BeanEditorMessages.duplicatePropertyName(
+                    _beanType,
+                    propertyName));
+
+        String label = defaultLabel(propertyName);
+
+        final PropertyAdapter adapter = _classPropertyAdapter.getPropertyAdapter(propertyName);
+
+        PropertyConduit conduit = defaultConduit(adapter);
+
+        PropertyEditModel propertyModel = new PropertyEditModelImpl(this, propertyName)
+                .label(label).conduit(conduit);
+
+        if (adapter != null)
+            propertyModel.propertyType(adapter.getType());
+
+        _properties.put(propertyName, propertyModel);
+
+        return propertyModel;
+    }
+
+    private PropertyConduit defaultConduit(final PropertyAdapter adapter)
+    {
+        if (adapter == null)
+            return null;
+
+        final Class propertyType = adapter.getType();
+
+        // Eventually, we'll find a way to replace this with something that does not
+        // use reflection.
+
+        return new PropertyConduit()
+        {
+            public Object read(Object instance)
+            {
+                return adapter.get(instance);
+            }
+
+            @SuppressWarnings("unchecked")
+            public void write(Object instance, Object value)
+            {
+                // TODO: Wrap this to provide a more useful error if the coercion
+                // fails.
+
+                Object coerced = _typeCoercer.coerce(value, propertyType);
+
+                adapter.set(instance, coerced);
+            }
+
+            public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+            {
+                T result = getAnnotation(adapter.getWriteMethod(), annotationClass);
+
+                if (result == null)
+                    result = getAnnotation(adapter.getReadMethod(), annotationClass);
+
+                return result;
+            }
+
+            private <T extends Annotation> T getAnnotation(Method method, Class<T> annotationClass)
+            {
+                return method == null ? null : method.getAnnotation(annotationClass);
+            }
+
+        };
+    }
+
+    public PropertyEditModel edit(String propertyName)
+    {
+        PropertyEditModel propertyModel = _properties.get(propertyName);
+
+        if (propertyModel == null)
+            throw new RuntimeException(BeanEditorMessages.unknownProperty(
+                    _beanType,
+                    propertyName,
+                    _properties.keySet()));
+
+        return propertyModel;
+    }
+
+    public PropertyEditModel get(String propertyName)
+    {
+        return edit(propertyName);
+    }
+
+    public List<String> getPropertyNames()
+    {
+        List<PropertyEditModel> propertyModels = newList(_properties.values());
+
+        Collections.sort(propertyModels, this);
+
+        List<String> result = newList();
+
+        for (PropertyEditModel propertyModel : propertyModels)
+            result.add(propertyModel.getPropertyName());
+
+        return result;
+    }
+
+    public int compare(PropertyEditModel o1, PropertyEditModel o2)
+    {
+        int result = o1.getOrder() - o2.getOrder();
+
+        if (result == 0)
+            result = o1.getPropertyName().compareTo(o2.getPropertyName());
+
+        return result;
+    }
+
+    private String defaultLabel(String propertyName)
+    {
+        String key = propertyName + "-label";
+
+        if (_messages.contains(key))
+            return _messages.get(key);
+
+        return TapestryUtils.toUserPresentable(propertyName);
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PrimitiveFieldConstraintGenerator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PrimitiveFieldConstraintGenerator.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PrimitiveFieldConstraintGenerator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PrimitiveFieldConstraintGenerator.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,38 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.beaneditor;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.services.ValidationConstraintGenerator;
+
+/**
+ * Adds a "required" constraint for any property of whose type is a primitive (not a wrapper or
+ * reference) type.
+ */
+public class PrimitiveFieldConstraintGenerator implements ValidationConstraintGenerator
+{
+    private final List<String> REQUIRED = Arrays.asList("required");
+
+    public List<String> buildConstraints(PropertyEditModel propertyModel)
+    {
+        Class type = propertyModel.getPropertyType();
+
+        return type.isPrimitive() ? REQUIRED : null;
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PropertyEditModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PropertyEditModelImpl.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PropertyEditModelImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/PropertyEditModelImpl.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,114 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.beaneditor;
+
+import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
+
+import org.apache.tapestry.beaneditor.BeanEditorModel;
+import org.apache.tapestry.beaneditor.PropertyConduit;
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.ioc.internal.util.Defense;
+
+public class PropertyEditModelImpl implements PropertyEditModel
+{
+    private final BeanEditorModel _model;
+
+    private final String _name;
+
+    private String _label;
+
+    private PropertyConduit _conduit;
+
+    private int _order;
+
+    private Class _propertyType = Object.class;
+
+    public PropertyEditModelImpl(BeanEditorModel model, final String name)
+    {
+        _model = model;
+        _name = name;
+    }
+
+    public Class getPropertyType()
+    {
+        return _propertyType;
+    }
+
+    public PropertyEditModel propertyType(Class propertyType)
+    {
+        Defense.notNull(propertyType, "propertyType");
+
+        _propertyType = propertyType;
+
+        return this;
+    }
+
+    public PropertyConduit getConduit()
+    {
+        return _conduit;
+    }
+
+    public PropertyEditModel conduit(PropertyConduit conduit)
+    {
+        _conduit = conduit;
+
+        return this;
+    }
+
+    public PropertyEditModel label(String label)
+    {
+        notBlank(label, "label");
+
+        _label = label;
+
+        return this;
+    }
+
+    public String getLabel()
+    {
+        return _label;
+    }
+
+    public String getPropertyName()
+    {
+        return _name;
+    }
+
+    public int getOrder()
+    {
+        return _order;
+    }
+
+    public PropertyEditModel order(int order)
+    {
+        _order = order;
+        return this;
+    }
+
+    public PropertyEditModel add(String propertyName)
+    {
+        return _model.add(propertyName);
+    }
+
+    public PropertyEditModel get(String propertyName)
+    {
+        return _model.get(propertyName);
+    }
+
+    public BeanEditorModel model()
+    {
+        return _model;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/ValidateAnnotationConstraintGenerator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/ValidateAnnotationConstraintGenerator.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/ValidateAnnotationConstraintGenerator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/beaneditor/ValidateAnnotationConstraintGenerator.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,40 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.beaneditor;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.beaneditor.Validate;
+import org.apache.tapestry.services.ValidationConstraintGenerator;
+
+/**
+ * Checks for the {@link Validate} annotation, and extracts its value to form the result.
+ */
+public class ValidateAnnotationConstraintGenerator implements ValidationConstraintGenerator
+{
+
+    public List<String> buildConstraints(PropertyEditModel propertyModel)
+    {
+        Validate annotation = propertyModel.getConduit().getAnnotation(Validate.class);
+
+        if (annotation == null)
+            return null;
+
+        return Arrays.asList(annotation.value().split(","));
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BeanEditorModelSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BeanEditorModelSourceImpl.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BeanEditorModelSourceImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BeanEditorModelSourceImpl.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,80 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.beaneditor.BeanEditorModel;
+import org.apache.tapestry.beaneditor.Order;
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.internal.beaneditor.BeanEditorModelImpl;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.internal.util.Defense;
+import org.apache.tapestry.ioc.services.ClassPropertyAdapter;
+import org.apache.tapestry.ioc.services.PropertyAccess;
+import org.apache.tapestry.ioc.services.PropertyAdapter;
+import org.apache.tapestry.ioc.services.TypeCoercer;
+import org.apache.tapestry.services.BeanEditorModelSource;
+
+public class BeanEditorModelSourceImpl implements BeanEditorModelSource
+{
+    private final TypeCoercer _typeCoercer;
+
+    private final PropertyAccess _propertyAccess;
+
+    public BeanEditorModelSourceImpl(final TypeCoercer typeCoercer,
+            final PropertyAccess propertyAccess)
+    {
+        _typeCoercer = typeCoercer;
+        _propertyAccess = propertyAccess;
+    }
+
+    public BeanEditorModel create(Class beanClass, ComponentResources resources)
+    {
+        Defense.notNull(beanClass, "beanClass");
+        Defense.notNull(resources, "resources");
+
+        Messages messages = resources.getMessages();
+
+        ClassPropertyAdapter adapter = _propertyAccess.getAdapter(beanClass);
+
+        BeanEditorModel model = new BeanEditorModelImpl(beanClass, adapter, _typeCoercer, messages);
+
+        for (String propertyName : adapter.getPropertyNames())
+        {
+            PropertyAdapter pa = adapter.getPropertyAdapter(propertyName);
+
+            if (pa.isRead() && pa.isUpdate())
+            {
+                PropertyEditModel propertyModel = model.add(propertyName);
+
+                Order annotation = propertyModel.getConduit().getAnnotation(Order.class);
+
+                if (annotation != null)
+                    propertyModel.order(annotation.value());
+            }
+        }
+
+        return model;
+    }
+
+    public List<String> buildConstraints(PropertyEditModel propertyModel)
+    {
+        return Collections.emptyList();
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CompositeFieldValidator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CompositeFieldValidator.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CompositeFieldValidator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CompositeFieldValidator.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,38 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import java.util.List;
+
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.ValidationException;
+
+/** Aggregates together a number of field validator instances as a single unit. */
+public final class CompositeFieldValidator implements FieldValidator
+{
+    private final FieldValidator[] _validators;
+
+    public CompositeFieldValidator(List<FieldValidator> validators)
+    {
+        _validators = validators.toArray(new FieldValidator[validators.size()]);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void validate(Object value) throws ValidationException
+    {
+        for (FieldValidator fv : _validators)
+            fv.validate(value);
+    }
+}
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorImpl.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorImpl.java Wed Jan 31 12:18:49 2007
@@ -42,7 +42,7 @@
     @SuppressWarnings("unchecked")
     public void validate(Object value) throws ValidationException
     {
-        if (_validator.skipIfBlank() && isBlank(value))
+        if (! _validator.invokeIfBlank() && isBlank(value))
             return;
 
         if (value != null && !_validator.getValueType().isInstance(value))

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java Wed Jan 31 12:18:49 2007
@@ -25,7 +25,6 @@
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Field;
 import org.apache.tapestry.FieldValidator;
-import org.apache.tapestry.ValidationException;
 import org.apache.tapestry.Validator;
 import org.apache.tapestry.ioc.MessageFormatter;
 import org.apache.tapestry.ioc.Messages;
@@ -56,42 +55,61 @@
         Component component = cast(field, Component.class, "field");
         notBlank(validatorType, "validatorType");
 
+        ComponentResources componentResources = component.getComponentResources();
+        String overrideId = componentResources.getId();
+        Locale locale = componentResources.getLocale();
+
+        ComponentResources containerResources = componentResources.getContainerResources();
+
+        // So, if you use a TextField on your EditUser page, we want to search the messages
+        // of the EditUser page (the container), not the TextField (which will always be the same).
+
+        Messages overrideMessages = containerResources.getMessages();
+
+        return createValidator(
+                field,
+                validatorType,
+                constraintValue,
+                overrideId,
+                overrideMessages,
+                locale);
+    }
+
+    public FieldValidator createValidator(Field field, String validatorType,
+            String constraintValue, String overrideId, Messages overrideMessages, Locale locale)
+    {
+        notBlank(validatorType, "validatorType");
+
         Validator validator = _validators.get(validatorType);
 
         if (validator == null)
-        {
-            String message = ServicesMessages.unknownValidatorType(validatorType, InternalUtils
-                    .sortedKeys(_validators));
-
-            throw new IllegalArgumentException(message);
-        }
+            throw new IllegalArgumentException(ServicesMessages.unknownValidatorType(
+                    validatorType,
+                    InternalUtils.sortedKeys(_validators)));
 
         Object coercedConstraintValue = coerceConstraintValue(constraintValue, validator
                 .getConstraintType());
 
-        MessageFormatter formatter = findMessageFormatter(component, validatorType, validator);
+        MessageFormatter formatter = findMessageFormatter(
+                overrideId,
+                overrideMessages,
+                locale,
+                validatorType,
+                validator);
 
         return new FieldValidatorImpl(field, coercedConstraintValue, formatter, validator);
     }
 
-    private MessageFormatter findMessageFormatter(Component component, String validatorType,
-            Validator validator)
+    private MessageFormatter findMessageFormatter(String overrideId, Messages overrideMessages,
+            Locale locale, String validatorType, Validator validator)
     {
-        ComponentResources resources = component.getComponentResources();
-
-        String overrideKey = resources.getId() + "-" + validatorType;
-
-        // So, if you use a TextField on your EditUser page, we want to search the messages
-        // of the EditUser page (the container), not the TextField (which will always be the same).
-
-        Messages messages = resources.getContainerResources().getMessages();
 
-        if (messages.contains(overrideKey))
-            return messages.getFormatter(overrideKey);
+        String overrideKey = overrideId + "-" + validatorType;
 
-        Locale locale = resources.getLocale();
+        if (overrideMessages.contains(overrideKey))
+            return overrideMessages.getFormatter(overrideKey);
 
-        messages = _messagesSource.getValidationMessages(locale);
+        Messages messages = _messagesSource.getValidationMessages(locale);
 
         String key = validator.getMessageKey();
 
@@ -113,17 +131,7 @@
         if (fieldValidators.size() == 1)
             return fieldValidators.get(0);
 
-        final FieldValidator[] array = fieldValidators.toArray(new FieldValidator[0]);
-
-        return new FieldValidator()
-        {
-            @SuppressWarnings("unchecked")
-            public void validate(Object value) throws ValidationException
-            {
-                for (FieldValidator fv : array)
-                    fv.validate(value);
-            }
-        };
+        return new CompositeFieldValidator(fieldValidators);
     }
 
     @SuppressWarnings("unchecked")

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidationConstraintGeneratorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidationConstraintGeneratorImpl.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidationConstraintGeneratorImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidationConstraintGeneratorImpl.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,48 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import java.util.List;
+
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry.services.ValidationConstraintGenerator;
+
+public class ValidationConstraintGeneratorImpl implements ValidationConstraintGenerator
+{
+    private final List<ValidationConstraintGenerator> _configuration;
+
+    public ValidationConstraintGeneratorImpl(final List<ValidationConstraintGenerator> configuration)
+    {
+        _configuration = configuration;
+    }
+
+    public List<String> buildConstraints(PropertyEditModel propertyModel)
+    {
+        List<String> result = CollectionFactory.newList();
+
+        for (ValidationConstraintGenerator g : _configuration)
+        {
+            List<String> constraints = g.buildConstraints(propertyModel);
+
+            if (constraints != null)
+                result.addAll(constraints);
+        }
+
+        // TODO: How to handle duplicate or conflicting constraints from different generators?
+
+        return result;
+    }
+}

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=501975&r1=501974&r2=501975
==============================================================================
--- 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 Wed Jan 31 12:18:49 2007
@@ -36,7 +36,7 @@
 import org.apache.tapestry.internal.services.ComponentEventImpl;
 import org.apache.tapestry.internal.services.EventImpl;
 import org.apache.tapestry.internal.services.Instantiator;
-import org.apache.tapestry.internal.util.AcceptVoidEventHandler;
+import org.apache.tapestry.internal.util.NotificationEventHandler;
 import org.apache.tapestry.ioc.BaseLocatable;
 import org.apache.tapestry.ioc.IOCUtilities;
 import org.apache.tapestry.ioc.Location;
@@ -997,7 +997,7 @@
         // Provide a default handler for when the provided handler is null.
 
         if (handler == null)
-            handler = new AcceptVoidEventHandler(eventType, _completeId);
+            handler = new NotificationEventHandler(eventType, _completeId);
 
         ComponentEvent event = new ComponentEventImpl(eventType, _id, context, handler,
                 _typeCoercer);

Copied: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/NotificationEventHandler.java (from r499593, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/AcceptVoidEventHandler.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/NotificationEventHandler.java?view=diff&rev=501975&p1=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/AcceptVoidEventHandler.java&r1=499593&p2=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/NotificationEventHandler.java&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/AcceptVoidEventHandler.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/NotificationEventHandler.java Wed Jan 31 12:18:49 2007
@@ -12,36 +12,38 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.util;
-
-import org.apache.tapestry.ComponentEventHandler;
-import org.apache.tapestry.runtime.Component;
-
-/**
- * A {@link ComponentEventHandler} that does not accept any value. This is used when an event is
- * dispatched as a notification. Event handlers should not return a value.
- */
-public class AcceptVoidEventHandler implements ComponentEventHandler
-{
-    private final String _eventType;
-
-    private final String _completeId;
-
-    public AcceptVoidEventHandler(final String eventType, final String completeId)
-    {
-        _eventType = eventType;
-        _completeId = completeId;
-    }
-
-    public boolean handleResult(Object result, Component component, String methodDescription)
-    {
-        // Don't even need a null check because this is only invoked if a method returns non-null.
-
-        throw new IllegalArgumentException(UtilMessages.noReturnValueAccepted(
-                _eventType,
-                _completeId,
-                result,
-                methodDescription));
-    }
-
-}
+package org.apache.tapestry.internal.util;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.runtime.Component;
+
+/**
+ * A {@link ComponentEventHandler} used for notification events. Event handler methods may return
+ * true (to abort the event) or false (to allow the event to continue bubbling up), but all other
+ * values are forbidden.
+ */
+public class NotificationEventHandler implements ComponentEventHandler
+{
+    private final String _eventType;
+
+    private final String _completeId;
+
+    public NotificationEventHandler(String eventType, String completeId)
+    {
+        _eventType = eventType;
+        _completeId = completeId;
+    }
+
+    public boolean handleResult(Object result, Component component, String methodDescription)
+    {
+        if (result instanceof Boolean)
+            return ((Boolean) result);
+
+        throw new IllegalArgumentException(UtilMessages.noReturnValueAccepted(
+                _eventType,
+                _completeId,
+                result,
+                methodDescription));
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BeanEditorModelSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BeanEditorModelSource.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BeanEditorModelSource.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BeanEditorModelSource.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,42 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.beaneditor.BeanEditorModel;
+import org.apache.tapestry.beaneditor.Order;
+
+/**
+ * Used by a component to create a default {@link BeanEditorModel} for a particular bean class. Also
+ * provides support to the model by generating validation information for individual fields.
+ */
+public interface BeanEditorModelSource
+{
+    /**
+     * Creates a new model used for editting the indicated bean class. The model will represent all
+     * read/write properties of the bean. The order of the properties is defined by the
+     * {@link Order} annotation on the getter or setter methods. The labels for the properties are
+     * derived from the property names, but if the component's message catalog has keys of the form
+     * <code>propertyName-label</code>, then those will be used instead.
+     * 
+     * @param beanClass
+     *            class of object to be editted
+     * @param resources
+     *            used when resolving resources, especially component messages (used to access
+     *            labels)
+     * @return a model
+     */
+    BeanEditorModel create(Class beanClass, ComponentResources resources);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/FieldValidatorSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/FieldValidatorSource.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/FieldValidatorSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/FieldValidatorSource.java Wed Jan 31 12:18:49 2007
@@ -14,9 +14,13 @@
 
 package org.apache.tapestry.services;
 
+import java.util.Locale;
+
 import org.apache.tapestry.Field;
 import org.apache.tapestry.FieldValidator;
 import org.apache.tapestry.Validator;
+import org.apache.tapestry.corelib.components.BeanEditor;
+import org.apache.tapestry.ioc.Messages;
 
 /**
  * Used to create {@link FieldValidator}s for a particular {@link Field} component.
@@ -24,7 +28,12 @@
 public interface FieldValidatorSource
 {
     /**
-     * Creates the validator.
+     * Creates the validator. The error message associated with the field validator usually comes
+     * from the {@link ValidationMessagesSource} (using the validator's
+     * {@link Validator#getMessageKey() message key}). However, if the container component of the
+     * field defines a message key <code><i>id</i>-<i>validator</i>
+     * (where id is the simple id of the field component, and validator is the validatorType), then that
+     * message is used instead. This allows you to override the message for a particular validation of a particular field.
      * 
      * @param field
      *            the field for which a validator is to be created
@@ -37,6 +46,34 @@
      * @return the field validator for the field
      */
     FieldValidator createValidator(Field field, String validatorType, String constraintValue);
+
+    /**
+     * Full featured version of {@link #createValidator(Field, String, String)} used in situations
+     * where the container of the field is not necesarrilly the place to look for override messages,
+     * and the id of the field is not the key to use when checking. The {@link BeanEditor} is an
+     * example of this.
+     * 
+     * @param field
+     *            the field for which a validator is to be created
+     * @param validatorType
+     *            used to select the {@link Validator} that forms the core of the
+     *            {@link FieldValidator}
+     * @param constraintValue
+     *            a value used to configure the validator, or null if the validator is not
+     *            configurable
+     * @param overrideId
+     *            the base id used when searching for validator message overrides (this would
+     *            normally be the field component's simple id)
+     * @param overrideMessages
+     *            the message catalog to search for override messages (this would normally be the
+     *            catalog for the container of the field component)
+     * @param locale
+     *            locale used when retrieving default validation messages from the
+     *            {@link ValidationMessagesSource}
+     * @return the field validator for the field
+     */
+    FieldValidator createValidator(Field field, String validatorType, String constraintValue,
+            String overrideId, Messages overrideMessages, Locale locale);
 
     /**
      * Creates a set of validators. The specification is a string used to identify and configure the

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=501975&r1=501974&r2=501975
==============================================================================
--- 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 Wed Jan 31 12:18:49 2007
@@ -40,10 +40,13 @@
 import org.apache.tapestry.annotations.CleanupRender;
 import org.apache.tapestry.annotations.InjectPage;
 import org.apache.tapestry.annotations.SetupRender;
+import org.apache.tapestry.beaneditor.Validate;
 import org.apache.tapestry.dom.DefaultMarkupModel;
 import org.apache.tapestry.dom.Document;
 import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.TapestryUtils;
+import org.apache.tapestry.internal.beaneditor.PrimitiveFieldConstraintGenerator;
+import org.apache.tapestry.internal.beaneditor.ValidateAnnotationConstraintGenerator;
 import org.apache.tapestry.internal.bindings.ComponentBindingFactory;
 import org.apache.tapestry.internal.bindings.LiteralBindingFactory;
 import org.apache.tapestry.internal.bindings.MessageBindingFactory;
@@ -57,6 +60,7 @@
 import org.apache.tapestry.internal.services.ApplicationStateWorker;
 import org.apache.tapestry.internal.services.AssetDispatcher;
 import org.apache.tapestry.internal.services.AssetSourceImpl;
+import org.apache.tapestry.internal.services.BeanEditorModelSourceImpl;
 import org.apache.tapestry.internal.services.BindingSourceImpl;
 import org.apache.tapestry.internal.services.ClasspathAssetAliasManagerImpl;
 import org.apache.tapestry.internal.services.CommonResourcesInjectionProvider;
@@ -128,6 +132,7 @@
 import org.apache.tapestry.internal.services.TranslatorSourceImpl;
 import org.apache.tapestry.internal.services.UnclaimedFieldWorker;
 import org.apache.tapestry.internal.services.UpdateListenerHub;
+import org.apache.tapestry.internal.services.ValidationConstraintGeneratorImpl;
 import org.apache.tapestry.internal.services.ValidationMessagesSourceImpl;
 import org.apache.tapestry.ioc.Configuration;
 import org.apache.tapestry.ioc.IOCUtilities;
@@ -474,6 +479,8 @@
                 locator,
                 ApplicationGlobals.class,
                 ApplicationStateManager.class,
+                ApplicationStatePersistenceStrategySource.class,
+                BeanEditorModelSource.class,
                 RequestGlobals.class,
                 MarkupWriterFactory.class,
                 PersistentFieldManager.class,
@@ -491,7 +498,8 @@
                 ValidationMessagesSource.class,
                 TranslatorSource.class,
                 TranslatorDefaultSource.class,
-                FieldValidatorSource.class);
+                FieldValidatorSource.class,
+                ValidationConstraintGenerator.class);
 
         configuration.add(new InfrastructureContribution("TypeCoercer", typeCoercer));
         configuration.add(new InfrastructureContribution("PropertyAccess", propertyAccess));
@@ -1119,9 +1127,37 @@
 
     public static ApplicationStateManager buildApplicationStateManager(
             Map<Class, ApplicationStateContribution> configuration,
-            @Inject("ApplicationStatePersistenceStrategySource")
+            @Inject("infrastructure:ApplicationStatePersistenceStrategySource")
             ApplicationStatePersistenceStrategySource source)
     {
         return new ApplicationStateManagerImpl(configuration, source);
+    }
+
+    public static BeanEditorModelSource buildBeanEditorModelSource(
+            @Inject("infrastructure:TypeCoercer")
+            TypeCoercer typeCoercer, @Inject("infrastructure:PropertyAccess")
+            PropertyAccess propertyAccess)
+    {
+        return new BeanEditorModelSourceImpl(typeCoercer, propertyAccess);
+    }
+
+    public static ValidationConstraintGenerator buildValidationConstraintGenerator(
+            List<ValidationConstraintGenerator> configuration)
+    {
+        return new ValidationConstraintGeneratorImpl(configuration);
+    }
+
+    /**
+     * Adds built-in constraint generators:
+     * <ul>
+     * <li>PrimtiveField -- primitive fields are always required
+     * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation
+     * </ul>
+     */
+    public static void contributeValidationConstraintGenerator(
+            OrderedConfiguration<ValidationConstraintGenerator> configuration)
+    {
+        configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator());
+        configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator());
     }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ValidationConstraintGenerator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ValidationConstraintGenerator.java?view=auto&rev=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ValidationConstraintGenerator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ValidationConstraintGenerator.java Wed Jan 31 12:18:49 2007
@@ -0,0 +1,43 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.services;
+
+import java.util.List;
+
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.beaneditor.PropertyEditModel;
+
+/**
+ * Invoked to generate a list of validation constraint strings for a property. This typically
+ * involves scanning the property for annotations or naming conventions that confer the desired
+ * validation. The constraint strings are ultimately handed to
+ * {@link FieldValidatorSource#createValidator(org.apache.tapestry.Field, String, String, String, org.apache.tapestry.ioc.Messages, java.util.Locale)}.
+ */
+public interface ValidationConstraintGenerator
+{
+    /**
+     * For a given property, identify all the approprite validation constraints. Each returned value
+     * is the name of a validator (i.e., "required") or a validator name and configuration (i.e.,
+     * "minlength=5"). These contraints are exactly the individual terms in a
+     * {@link FieldValidatorSource#createValidators(org.apache.tapestry.Field, String) validate specification}.
+     * These will ultimately be used to create {@link FieldValidator}s for the field that edits the
+     * property.
+     * 
+     * @param propertyModel
+     * @return
+     * @see FieldValidatorSource
+     */
+    List<String> buildConstraints(PropertyEditModel propertyModel);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Wed Jan 31 12:18:49 2007
@@ -18,6 +18,7 @@
 import static org.apache.tapestry.internal.test.CodeEq.codeEq;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
 import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.isA;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -47,6 +48,8 @@
 import org.apache.tapestry.Validator;
 import org.apache.tapestry.annotations.Inject;
 import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.beaneditor.PropertyConduit;
+import org.apache.tapestry.beaneditor.PropertyEditModel;
 import org.apache.tapestry.ioc.Location;
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.Resource;
@@ -717,9 +720,9 @@
         expect(validator.getValueType()).andReturn(valueType).atLeastOnce();
     }
 
-    protected final void train_skipIfBlank(Validator validator, boolean skipIfBlank)
+    protected final void train_invokeIfBlank(Validator validator, boolean invokeIfBlank)
     {
-        expect(validator.skipIfBlank()).andReturn(skipIfBlank).atLeastOnce();
+        expect(validator.invokeIfBlank()).andReturn(invokeIfBlank).atLeastOnce();
     }
 
     protected final void train_getFieldPersistenceStrategy(ComponentModel model, String fieldName,
@@ -758,5 +761,35 @@
             String strategyName, ApplicationStatePersistenceStrategy strategy)
     {
         expect(source.get(strategyName)).andReturn(strategy).atLeastOnce();
+    }
+
+    protected final void train_get(Messages messages, String key, String message)
+    {
+        expect(messages.get(key)).andReturn(message).atLeastOnce();
+    }
+
+    protected final void stub_contains(Messages messages, boolean contained)
+    {
+        expect(messages.contains(isA(String.class))).andStubReturn(contained);
+    }
+
+    protected final <T extends Annotation> void train_getAnnotation(PropertyConduit conduit, Class<T> annotationClass, T annotation)
+    {
+        expect(conduit.getAnnotation(annotationClass)).andReturn(annotation).atLeastOnce();
+    }
+
+    protected final void train_getConduit(PropertyEditModel model, PropertyConduit conduit)
+    {
+        expect(model.getConduit()).andReturn(conduit).atLeastOnce();
+    }
+
+    protected final PropertyConduit newPropertyConduit()
+    {
+        return newMock(PropertyConduit.class);
+    }
+
+    protected final PropertyEditModel newPropertyEditModel()
+    {
+        return newMock(PropertyEditModel.class);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Max.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Max.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Max.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Max.java Wed Jan 31 12:18:49 2007
@@ -37,9 +37,9 @@
         return Number.class;
     }
 
-    public boolean skipIfBlank()
+    public boolean invokeIfBlank()
     {
-        return true;
+        return false;
     }
 
     public void validate(Field field, Long constraintValue, MessageFormatter formatter, Number value)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MaxLength.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MaxLength.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MaxLength.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MaxLength.java Wed Jan 31 12:18:49 2007
@@ -36,9 +36,9 @@
         return String.class;
     }
 
-    public boolean skipIfBlank()
+    public boolean invokeIfBlank()
     {
-        return true;
+        return false;
     }
 
     public void validate(Field field, Integer constraintValue, MessageFormatter formatter, String value)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Min.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Min.java?view=diff&rev=501975&r1=501974&r2=501975
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Min.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Min.java Wed Jan 31 12:18:49 2007
@@ -37,9 +37,9 @@
         return Number.class;
     }
 
-    public boolean skipIfBlank()
+    public boolean invokeIfBlank()
     {
-        return true;
+        return false;
     }
 
     public void validate(Field field, Long constraintValue, MessageFormatter formatter, Number value)