You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2007/07/29 22:49:41 UTC

svn commit: r560813 - in /tapestry/tapestry4/trunk/tapestry-framework/src: java/org/apache/tapestry/ java/org/apache/tapestry/enhance/ java/org/apache/tapestry/pageload/ java/org/apache/tapestry/util/io/ js/tapestry/ js/tapestry/form/ test/org/apache/t...

Author: jkuhnert
Date: Sun Jul 29 13:49:40 2007
New Revision: 560813

URL: http://svn.apache.org/viewvc?view=rev&rev=560813
Log:
-) Compacted form related js files in to a single file.

-) Minor performance enhancements for parameter property binding enhancements.

-) Minor code cleanup/styling.

Removed:
    tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form/
    tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form_compat.js
Modified:
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/Tapestry.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/ComponentClassProviderContext.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/NamespaceClassSearchComponentClassProvider.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/SerializableAdaptor.java
    tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form.js
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/enhance/TestParameterPropertyWorker.java

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/Tapestry.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/Tapestry.java?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/Tapestry.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/Tapestry.java Sun Jul 29 13:49:40 2007
@@ -14,20 +14,6 @@
 
 package org.apache.tapestry;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.ResourceBundle;
-import java.util.Set;
-
 import org.apache.hivemind.ApplicationRuntimeException;
 import org.apache.hivemind.Location;
 import org.apache.tapestry.event.ChangeObserver;
@@ -36,10 +22,15 @@
 import org.apache.tapestry.spec.IComponentSpecification;
 import org.apache.tapestry.util.StringSplitter;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.*;
+
 /**
  * A placeholder for a number of (static) methods that don't belong elsewhere, as well as a global
  * location for static constants.
- * 
+ *
  * @since 1.0.1
  * @author Howard Lewis Ship
  */
@@ -62,12 +53,12 @@
     /**
      * Almost identical to the direct service, except specifically for handling
      * browser level events.
-     * 
+     *
      * @since 4.1
      */
-    
+
     public static final String DIRECT_EVENT_SERVICE = "directevent";
-    
+
     /**
      * The name ("external") of a service that a allows {@link IExternalPage} to be selected.
      * Associated with a {@link org.apache.tapestry.link.ExternalLink} component.
@@ -121,13 +112,13 @@
      */
 
     public static final String RESET_SERVICE = "reset";
-    
+
     /**
      * Property name used to get the extension used for templates. This may be set in the page or
      * component specification, or in the page (or component's) immediate container (library or
      * application specification). Unlike most properties, value isn't inherited all the way up the
      * chain. The default template extension is "html".
-     * 
+     *
      * @since 3.0
      */
 
@@ -144,7 +135,7 @@
     /**
      * Suffix appended to a parameter name to form the name of a property that stores the binding
      * for the parameter.
-     * 
+     *
      * @since 3.0
      */
 
@@ -153,7 +144,7 @@
     /**
      * Key used to obtain an extension from the application specification. The extension, if it
      * exists, implements {@link org.apache.tapestry.request.IRequestDecoder}.
-     * 
+     *
      * @since 2.2
      */
 
@@ -164,7 +155,7 @@
      * extension must implement {@link org.apache.tapestry.multipart.IMultipartDecoder} (and is
      * generally a configured instance of
      * {@link IMultipartDecoder}).
-     * 
+     *
      * @since 3.0
      */
 
@@ -172,7 +163,7 @@
 
     /**
      * Method id used to check that {@link IPage#validate(IRequestCycle)} is invoked.
-     * 
+     *
      * @see #checkMethodInvocation(Object, String, Object)
      * @since 3.0
      */
@@ -181,7 +172,7 @@
 
     /**
      * Method id used to check that {@link IPage#detach()} is invoked.
-     * 
+     *
      * @see #checkMethodInvocation(Object, String, Object)
      * @since 3.0
      */
@@ -192,7 +183,7 @@
      * Regular expression defining a simple property name. Used by several different parsers. Simple
      * property names match Java variable names; a leading letter (or underscore), followed by
      * letters, numbers and underscores.
-     * 
+     *
      * @since 3.0
      */
 
@@ -211,10 +202,10 @@
     public static final String VERSION = readVersion();
 
     private static final String UNKNOWN_VERSION = "Unknown";
-    
+
     /**
      * Contains strings loaded from TapestryStrings.properties.
-     * 
+     *
      * @since 1.0.8
      */
 
@@ -242,7 +233,7 @@
 
     private static final ThreadLocal _invokedMethodIds = new ThreadLocal();
 
-    
+
     /**
      * Prevent instantiation.
      */
@@ -337,7 +328,7 @@
 
     /**
      * Closes the stream (if not null), ignoring any {@link IOException}thrown.
-     * 
+     *
      * @since 1.0.2
      */
 
@@ -359,7 +350,7 @@
     /**
      * Gets a string from the TapestryStrings resource bundle. The string in the bundle is treated
      * as a pattern for {@link MessageFormat#format(java.lang.String, java.lang.Object[])}.
-     * 
+     *
      * @since 1.0.8
      */
 
@@ -378,7 +369,7 @@
 
     /**
      * Convienience method for invoking {@link #format(String, Object[])}.
-     * 
+     *
      * @since 3.0
      */
 
@@ -389,38 +380,38 @@
 
     /**
      * Convienience method for invoking {@link #format(String, Object[])}.
-     * 
+     *
      * @since 3.0
      */
 
     public static String format(String key, Object arg)
     {
         return format(key, new Object[]
-        { arg });
+          { arg });
     }
 
     /**
      * Convienience method for invoking {@link #format(String, Object[])}.
-     * 
+     *
      * @since 3.0
      */
 
     public static String format(String key, Object arg1, Object arg2)
     {
         return format(key, new Object[]
-        { arg1, arg2 });
+          { arg1, arg2 });
     }
 
     /**
      * Convienience method for invoking {@link #format(String, Object[])}.
-     * 
+     *
      * @since 3.0
      */
 
     public static String format(String key, Object arg1, Object arg2, Object arg3)
     {
         return format(key, new Object[]
-        { arg1, arg2, arg3 });
+          { arg1, arg2, arg3 });
     }
 
     /**
@@ -453,7 +444,7 @@
 
     /**
      * Returns the size of a collection, or zero if the collection is null.
-     * 
+     *
      * @since 2.2
      */
 
@@ -467,7 +458,7 @@
 
     /**
      * Returns the length of the array, or 0 if the array is null.
-     * 
+     *
      * @since 2.2
      */
 
@@ -481,7 +472,7 @@
 
     /**
      * Returns true if the Map is null or empty.
-     * 
+     *
      * @since 3.0
      */
 
@@ -492,7 +483,7 @@
 
     /**
      * Returns true if the Collection is null or empty.
-     * 
+     *
      * @since 3.0
      */
 
@@ -508,7 +499,7 @@
      * representation as an array will encode more efficiently (via
      * {@link org.apache.tapestry.util.io.DataSqueezerImpl} than serializing the Map and its
      * contents.
-     * 
+     *
      * @return the array of keys and values, or null if the input Map is null or empty
      * @since 2.2
      */
@@ -537,7 +528,7 @@
 
     /**
      * Converts an even-sized array of objects back into a {@link Map}.
-     * 
+     *
      * @see #convertMapToArray(Map)
      * @return a Map, or null if the array is null or empty
      * @since 2.2
@@ -564,10 +555,10 @@
 
         return result;
     }
-    
+
     /**
      * Creates an exception indicating the binding value is null.
-     * 
+     *
      * @since 3.0
      */
 
@@ -579,35 +570,34 @@
     /** @since 3.0 * */
 
     public static ApplicationRuntimeException createNoSuchComponentException(IComponent component,
-            String id, Location location)
+                                                                             String id, Location location)
     {
-        return new ApplicationRuntimeException(format("no-such-component", component
-                .getExtendedId(), id), component, location, null);
+        return new ApplicationRuntimeException(format("no-such-component", component.getExtendedId(), id),
+                                               component, location, null);
     }
 
     /** @since 3.0 * */
 
     public static BindingException createRequiredParameterException(IComponent component,
-            String parameterName)
+                                                                    String parameterName)
     {
-        return new BindingException(format("required-parameter", parameterName, component
-                .getExtendedId()), component, null, component.getBinding(parameterName), null);
+        return new BindingException(format("required-parameter", parameterName, component.getExtendedId()),
+                                    component, null, component.getBinding(parameterName), null);
     }
 
     /** @since 3.0 * */
 
     public static ApplicationRuntimeException createRenderOnlyPropertyException(
-            IComponent component, String propertyName)
+      IComponent component, String propertyName)
     {
-        return new ApplicationRuntimeException(format(
-                "render-only-property",
-                propertyName,
-                component.getExtendedId()), component, null, null);
+        return new ApplicationRuntimeException(format("render-only-property",
+                                                      propertyName,
+                                                      component.getExtendedId()), component, null, null);
     }
 
     /**
      * Clears the list of method invocations.
-     * 
+     *
      * @see #checkMethodInvocation(Object, String, Object)
      * @since 3.0
      */
@@ -620,7 +610,7 @@
     /**
      * Adds a method invocation to the list of invocations. This is done in a super-class
      * implementations.
-     * 
+     *
      * @see #checkMethodInvocation(Object, String, Object)
      * @since 3.0
      */
@@ -648,7 +638,7 @@
      * the super-class implementation was invoked.
      * <p>
      * The list of method invocations is stored in a {@link ThreadLocal} variable.
-     * 
+     *
      * @since 3.0
      */
 
@@ -659,15 +649,14 @@
         if (methodIds != null && methodIds.contains(methodId))
             return;
 
-        throw new ApplicationRuntimeException(Tapestry.format(
-                "Tapestry.missing-method-invocation",
-                object.getClass().getName(),
-                methodName));
+        throw new ApplicationRuntimeException(Tapestry.format("Tapestry.missing-method-invocation",
+                                                              object.getClass().getName(),
+                                                              methodName));
     }
 
     /**
      * Method used by pages and components to send notifications about property changes.
-     * 
+     *
      * @param component
      *            the component containing the property
      * @param propertyName

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java Sun Jul 29 13:49:40 2007
@@ -155,12 +155,15 @@
         body.begin();
         
         String bindingFieldName = fieldName + "$Binding";
+        String bindingCheckedName = bindingFieldName + "Checked";
 
         op.addField(bindingFieldName, IBinding.class);
+        op.addField(bindingCheckedName, Boolean.TYPE);
 
-        body.addln("if ({0} == null)", bindingFieldName);
+        body.addln("if (!{0})", bindingCheckedName);
         body.begin();
         body.addln("{0} = getBinding(\"{1}\");", bindingFieldName, parameterName);
+        body.addln("{0} = true;", bindingCheckedName);
         body.end();
 
         body.addln("return {0};", bindingFieldName);

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/ComponentClassProviderContext.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/ComponentClassProviderContext.java?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/ComponentClassProviderContext.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/ComponentClassProviderContext.java Sun Jul 29 13:49:40 2007
@@ -32,8 +32,7 @@
 
     private IComponentSpecification _specification;
 
-    public ComponentClassProviderContext(String pageName,
-            IComponentSpecification pageSpecification, INamespace namespace)
+    public ComponentClassProviderContext(String pageName, IComponentSpecification pageSpecification, INamespace namespace)
     {
         Defense.notNull(pageName, "pageName");
         Defense.notNull(pageSpecification, "pageSpecification");

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/NamespaceClassSearchComponentClassProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/NamespaceClassSearchComponentClassProvider.java?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/NamespaceClassSearchComponentClassProvider.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/pageload/NamespaceClassSearchComponentClassProvider.java Sun Jul 29 13:49:40 2007
@@ -25,8 +25,7 @@
  * @author Howard M. Lewis Ship
  * @since 4.0
  */
-public class NamespaceClassSearchComponentClassProvider implements
-        ComponentClassProvider
+public class NamespaceClassSearchComponentClassProvider implements ComponentClassProvider
 {
 
     /**
@@ -38,8 +37,7 @@
 
     private ClassFinder _classFinder;
 
-    public String provideComponentClassName(
-            ComponentClassProviderContext context)
+    public String provideComponentClassName(ComponentClassProviderContext context)
     {
         INamespace namespace = context.getNamespace();
         String packages = namespace.getPropertyValue(_packagesName);

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/SerializableAdaptor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/SerializableAdaptor.java?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/SerializableAdaptor.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/SerializableAdaptor.java Sun Jul 29 13:49:40 2007
@@ -14,22 +14,15 @@
 
 package org.apache.tapestry.util.io;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-
 import org.apache.commons.codec.binary.Base64;
 import org.apache.hivemind.ApplicationRuntimeException;
 import org.apache.hivemind.ClassResolver;
 import org.apache.tapestry.services.DataSqueezer;
 
+import java.io.*;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
 /**
  * The most complicated of the adaptors, this one takes an arbitrary serializable object, serializes
  * it to binary (possibly compressing the stream along the way), and encodes it in a Base64
@@ -84,8 +77,7 @@
 
             byte[] encoded = Base64.encodeBase64(byteArray);
 
-            String prefix = Character.toString(useCompressed ? GZIP_BYTESTREAM_PREFIX
-                    : BYTESTREAM_PREFIX);
+            String prefix = Character.toString(useCompressed ? GZIP_BYTESTREAM_PREFIX  : BYTESTREAM_PREFIX);
 
             return prefix + new String(encoded);
         }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form.js?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form.js (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form.js Sun Jul 29 13:49:40 2007
@@ -440,5 +440,372 @@
 	}
 }
 
-dojo.require("tapestry.form.validation");
-dojo.require("tapestry.form_compat");
+dojo.provide("tapestry.form.validation");
+dojo.require("dojo.validate.check");
+dojo.require("dojo.html.style");
+
+tapestry.form.validation={
+
+	missingClass:"fieldMissing", // default css class that will be applied to fields missing a value
+	invalidClass:"fieldInvalid", // default css class applied to fields with invalid data
+
+	dialogName:"tapestry:AlertDialog",
+
+	/**
+	 * Main entry point for running form validation. The
+	 * props object passed in contains a number of fields that
+	 * are managed by tapestry.form:
+	 *
+	 * 		props = {
+	 * 			validateForm:[true|false] // whether to run validation at all
+	 * 			profiles:[profile1, profile2] // set of dojo.validate.check() style profiles
+	 * 										  // that may have been registered with form
+	 * 		}
+	 *
+	 * The individual profiles will contain any of the data described by the dojo documentation
+	 * for dojo.validate.check(). In addition to that, each profile will also have a corresponding
+	 * string message to display if the specified condition has been met. For example, if you have
+	 * specified that a select field named "select1" was required your profile would look something
+	 * like:
+	 *
+	 * 		profile = {
+	 * 			"required":["select1"], // normal dojo.validate.check data
+	 * 			"select1":{ // tapestry field/error type specific data
+	 * 				"required":"You must select a value for select1."
+	 * 			}
+	 * 		}
+	 *
+	 * It is intended for you to call dojo.validate.check(form, profile) for each profile
+	 * stored in the "profiles" field, as well as deciding how to display errors / warnings.
+	 *
+	 * @return Boolean indicating if form submission should continue. If false the form
+	 * 			will ~not~ be submitted.
+	 */
+	validateForm:function(form, props){
+		if (typeof form == "undefined") {return false;}
+		if (typeof props == "undefined") {return true;} // form exists but no profile? just submit I guess..
+		if (!props.validateForm) {return true;}
+
+		try {
+			this.clearValidationDecorations(form, props);
+
+			for (var i=0; i < props.profiles.length; i++) {
+				var results=dojo.validate.check(form, props.profiles[i]);
+
+				if (!this.processResults(form, results, props.profiles[i])) {
+					this.summarizeErrors(form, results, props.profiles[i]);
+					return false;
+				}
+			}
+		} catch (e) {
+			// since so many dynamic function calls may happen in here it's best that we
+			// catch all of them and log them or else peoples forms might still get submitted
+			// and they'd never be able to figure out what was wrong
+			dojo.log.exception("Error validating", e, true);
+			return false;
+		}
+
+		return true;
+	},
+
+	/**
+	 * Called for each registered profile on a form after
+	 * dojo.validate.check() has been called. This function is
+	 * expected to do UI related notifications of fields in error.
+	 *
+	 * @param form The form that was validated.
+	 * @param results The result of calling dojo.validate.check(form,profile)
+	 * @param profile The original profile used to validate form, also holds
+	 * 				  validation error messages to be used for each field.
+	 *
+	 * @return Boolean, if false form should not be submitted and all validation
+	 * 		   should be stopped. If true validation will continue and eventually
+	 * 		   form will be submitted.
+	 */
+	processResults:function(form, results, profile){
+		if (results.isSuccessful()) { return true; }
+
+		var formValid=true;
+		if (results.hasMissing()) {
+			var missing=results.getMissing();
+			for (var i=0; i < missing.length; i++) {
+				this.handleMissingField(missing[i], profile);
+			}
+
+			formValid=false;
+		}
+
+		if (results.hasInvalid()) {
+			var invalid=results.getInvalid();
+			for (var i=0; i < invalid.length; i++) {
+				this.handleInvalidField(invalid[i], profile);
+			}
+
+			formValid=false;
+		}
+
+		return formValid; // if got past successful everything is invalid
+	},
+
+	/**
+	 * Default field decorator for missing fields.
+	 *
+	 * @param field The field element that was missing data.
+	 * @param profile The form validation profile.
+	 */
+	handleMissingField:function(field, profile){
+		field=dojo.byId(field);
+		if (dj_undef("type", field)) {return;}
+		dojo.html.removeClass(field, this.invalidClass);
+
+		if (!dojo.html.hasClass(field, this.missingClass)){
+			dojo.html.prependClass(field, this.missingClass);
+		}
+	},
+
+	/**
+	 * Default field decorator for invalid fields.
+	 *
+	 * @param field The field element that had invalid data.
+	 * @param profile The form validation profile.
+	 */
+	handleInvalidField:function(field, profile){
+		field=dojo.byId(field);
+		if (dj_undef("type", field)) {return;}
+		dojo.html.removeClass(field, this.missingClass);
+
+		if (!dojo.html.hasClass(field, this.invalidClass)){
+			dojo.html.prependClass(field, this.invalidClass);
+		}
+	},
+
+	/**
+	 * Clears out previous css classes set on fields
+	 * in error.
+	 */
+	clearValidationDecorations:function(form, props){
+
+        for (var i=0; i < props.profiles.length; i++) {
+
+            for (var fieldName in props.profiles[i]) {
+                if (dj_undef("type", form.elements[fieldName]) || typeof form.elements[fieldName].type == "undefined"
+                        || form.elements[fieldName].type == "submit"
+                        || form.elements[fieldName].type == "hidden") { continue; }
+
+                dojo.html.removeClass(form.elements[fieldName], this.missingClass);
+                dojo.html.removeClass(form.elements[fieldName], this.invalidClass);
+            }
+        }
+	},
+
+	/**
+	 * Optionally allows an alert dialog/dhtml dialog/etc to
+	 * be displayed to user to alert them to the invalid state
+	 * of their form if validation errors have occurred.
+	 *
+	 * @param form The form being validated.
+	 * @param results Returned value of dojo.validate.check(form, profile)
+	 * @param profile Validation profile definition
+	 */
+	summarizeErrors:function(form, results, profile){
+		var merrs=[];
+		var ierrs=[];
+		tapestry.form.currentFocus=null;
+
+		if (results.hasMissing()){
+			var fields=results.getMissing();
+			for (var i=0; i<fields.length; i++){
+				if(i==0 && !tapestry.form.currentFocus){
+					tapestry.form.currentFocus=fields[i];
+				}
+				if (profile[fields[i]] && profile[fields[i]]["required"]){
+					if (dojo.lang.isArray(profile[fields[i]]["required"])) {
+						for (var z=0; z < profile[fields[i]]["required"].length; z++)
+							merrs.push(profile[fields[i]]["required"][z]);
+					} else
+						merrs.push(profile[fields[i]]["required"]);
+				}
+			}
+		}
+		if (results.hasInvalid()){
+			var fields=results.getInvalid();
+			for (var i=0; i<fields.length; i++){
+				if(i==0 && !tapestry.form.currentFocus){
+					tapestry.form.currentFocus=fields[i];
+				}
+				if (profile[fields[i]] && profile[fields[i]]["constraints"]){
+					if (dojo.lang.isArray(profile[fields[i]]["constraints"])) {
+						for (var z=0; z < profile[fields[i]]["constraints"].length; z++)
+							ierrs.push(profile[fields[i]]["constraints"][z]);
+					} else
+						ierrs.push(profile[fields[i]]["constraints"]);
+				}
+			}
+		}
+
+		var msg="";
+		if (merrs.length > 0) {
+			msg+='<ul class="missingList">';
+			for (var i=0; i<merrs.length;i++) {
+				msg+="<li>"+merrs[i]+"</li>";
+			}
+			msg+="</ul>";
+		}
+		if (ierrs.length > 0) {
+			msg+='<ul class="invalidList">';
+			for (var i=0; i<ierrs.length;i++) {
+				msg+="<li>"+ierrs[i]+"</li>";
+			}
+			msg+="</ul>";
+		}
+
+        dojo.require("dojo.widget.*");
+        dojo.require("tapestry.widget.AlertDialog");
+
+        var ad=dojo.widget.byId("validationDialog");
+		if (ad) {
+			ad.setMessage(msg);
+			ad.show();
+			return;
+		}
+
+		var node=document.createElement("span");
+		document.body.appendChild(node);
+		var dialog=dojo.widget.createWidget(this.dialogName,
+						{
+							widgetId:"validationDialog",
+							message:msg
+						}, node);
+		dialog.show();
+	},
+
+	/**
+	 * Validates that the input value matches the given
+	 * regexp pattern.
+	 *
+	 * @param value The string value to be evaluated.
+	 * @param pattern The regexp pattern used to match against value.
+	 */
+	isValidPattern:function(value, pattern){
+		if (typeof value != "string" || typeof pattern != "string") { return false; }
+
+		var re = new RegExp(pattern);
+		return re.test(value);
+	},
+
+	isPalleteSelected:function(elem){
+		return elem.length > 0;
+	},
+
+   /**
+    * Validates that the input value is equal with the value of the given input control.
+    */
+    isEqual:function(value, other){
+        var otherValue = dojo.byId(other).value;
+        return value == otherValue;
+    },
+
+   /**
+    * Validates that the input value is not equal with the value of the given input control.
+    */
+    isNotEqual:function(value, other){
+        return !tapestry.form.validation.isEqual(value, other);
+    },
+
+   /**
+    *  Checks that the value given is greater than or equal to the value of
+    *  minString. Uses dojo.i18n.number.parse() to parse out the values using
+    *  the locale settings configured for the current page.
+    */
+    greaterThanOrEqual:function(value, minString, flags){
+        flags.validate=false;
+        var min = dojo.i18n.number.parse(minString, null, flags);
+        var num = dojo.i18n.number.parse(value, null, flags);
+        if (Number.NaN == num) { return false; }
+
+        return num >= min;
+    },
+
+   /**
+    *  Checks that the value given is less than or equal to the value of
+    *  maxString. Uses dojo.i18n.number.parse() to parse out the values using
+    *  the locale settings configured for the current page.
+    */
+    lessThanOrEqual:function(value, maxString, flags){
+        flags.validate=false;
+        var max = dojo.i18n.number.parse(maxString, null, flags);
+        var num = dojo.i18n.number.parse(value, null, flags);
+        if (Number.NaN == num) { return false; }
+
+        return num <= max;
+    }
+}
+
+dojo.provide("tapestry.form.datetime");
+dojo.require("dojo.date.format");
+dojo.require("dojo.validate.datetime");
+
+tapestry.form.datetime={
+
+	/**
+	 * Checks if the specified value is a valid date, according to
+	 * the flags passed in.
+	 *
+	 * @param value The string value of the date being validated.
+	 * @param flags An object.
+	 * 		flags.format 	A string format pattern that will be used to validate
+	 * 						the incoming value via @link dojo.validate.isValidDate(value, format).
+	 * 		flags.max		A string date value representing the maximum date that can be selected.
+	 * 		flags.min		A string date value representing the minimum date that can be selected.
+	 * @return Boolean. True if valid, false otherwise.
+	 */
+	isValidDate:function(value, flags){
+		if(!value){return false;}
+
+		if (!flags){
+			dojo.raise("isValidDate: value and flags must be specified");
+			return;
+		}
+
+		// parse date value
+		var dateValue=null;
+		try {
+			dateValue = dojo.date.parse(value, flags);
+		} catch (e) {
+			dojo.log.exception("Error parsing input date.", e, true);
+			return false;
+		}
+
+		if(dateValue == null) { return false; }
+
+		// convert to format that is validatable
+		value=dojo.date.format(dateValue, flags);
+
+		// TODO: This is totally useless right now, doesn't even accept formats with string equivs
+		// See a better method http://www.mattkruse.com/javascript/date/source.html
+		// basic format validation
+		// if (!dojo.validate.isValidDate(value, flags.format))
+		//	return false;
+
+		// max date
+		if (!dj_undef("max", flags)){
+			if (typeof flags.max == "string"){
+				flags.max=dojo.date.parse(flags.max, flags);
+			}
+			if (dojo.date.compare(dateValue, flags.max, dojo.date.compareTypes.DATE) > 0)
+				return false;
+		}
+
+		// min date
+		if (!dj_undef("min", flags)){
+			if (typeof flags.min == "string"){
+				flags.min=dojo.date.parse(flags.min, flags);
+			}
+			if (dojo.date.compare(dateValue, flags.min, dojo.date.compareTypes.DATE) < 0)
+				return false;
+		}
+
+		return true;
+	}
+
+}

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/enhance/TestParameterPropertyWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/enhance/TestParameterPropertyWorker.java?view=diff&rev=560813&r1=560812&r2=560813
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/enhance/TestParameterPropertyWorker.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/enhance/TestParameterPropertyWorker.java Sun Jul 29 13:49:40 2007
@@ -131,29 +131,29 @@
 
     public void test_Standard()
     {
-        IComponentSpecification spec = buildComponentSpecification("fred", buildParameterSpec(
-          "fred",
-          null,
-          null));
+        IComponentSpecification spec =
+          buildComponentSpecification("fred", buildParameterSpec("fred", null, null));
 
         EnhancementOperation op = newMock(EnhancementOperation.class);
 
         expect(op.getPropertyType("fred")).andReturn(String.class);
-
         op.claimProperty("fred");
 
         String bindingFieldName = "_$fred$Binding";
+        String bindingChecked = bindingFieldName + "Checked";
 
         op.addField("_$fred", String.class);
         op.addField("_$fred$Default", String.class);
         op.addField("_$fred$Cached", boolean.class);
-        op.addField("_$fred$Binding", IBinding.class);
+        op.addField(bindingFieldName, IBinding.class);
+        op.addField(bindingChecked, Boolean.TYPE);
 
         BodyBuilder builder = new BodyBuilder();
         builder.begin();
-        builder.addln("if ({0} == null)", bindingFieldName);
+        builder.addln("if (!{0})", bindingChecked);
         builder.begin();
         builder.addln("{0} = getBinding(\"{1}\");", bindingFieldName, "fred");
+        builder.addln("{0} = true;", bindingChecked);
         builder.end();
         builder.addln("return {0};", bindingFieldName);
         builder.end();
@@ -195,8 +195,7 @@
         builder.end();
 
         builder.addln("if (get_$fred$Binding() == null)");
-        builder
-          .addln("  throw new org.apache.hivemind.ApplicationRuntimeException(\"Parameter 'fred' is not bound and can not be updated.\");");
+        builder.addln("  throw new org.apache.hivemind.ApplicationRuntimeException(\"Parameter 'fred' is not bound and can not be updated.\");");
 
         builder.addln("get_$fred$Binding().setObject(($w) $1);");
 
@@ -249,17 +248,20 @@
         op.claimProperty("fred");
 
         String bindingFieldName = "_$fred$Binding";
+        String bindingChecked = bindingFieldName + "Checked";
 
         op.addField("_$fred", String.class);
         op.addField("_$fred$Default", String.class);
         op.addField("_$fred$Cached", boolean.class);
-        op.addField("_$fred$Binding", IBinding.class);
+        op.addField(bindingFieldName, IBinding.class);
+        op.addField(bindingChecked, Boolean.TYPE);
 
         BodyBuilder builder = new BodyBuilder();
         builder.begin();
-        builder.addln("if ({0} == null)", bindingFieldName);
+        builder.addln("if (!{0})", bindingChecked);
         builder.begin();
         builder.addln("{0} = getBinding(\"{1}\");", bindingFieldName, "myparam");
+        builder.addln("{0} = true;", bindingChecked);
         builder.end();
         builder.addln("return {0};", bindingFieldName);
         builder.end();