struts-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gvanma...@apache.org
Subject svn commit: r394829 - in /struts/shale/trunk/core-library/src: conf/ java/org/apache/shale/component/ java/org/apache/shale/resources/ java/org/apache/shale/taglib/ java/org/apache/shale/validator/ test/org/apache/shale/validator/
Date Tue, 18 Apr 2006 03:21:35 GMT
Author: gvanmatre
Date: Mon Apr 17 20:21:33 2006
New Revision: 394829

URL: http://svn.apache.org/viewcvs?rev=394829&view=rev
Log:
Fixes for bugs 39121 and 38079 reported by Luca Conte. Fix for bug 39121 breaks backwards compatibility with version 1.2 of commons validator.  Version 1.3 of commons validator is required.  The requirements for registering validation rules has also changed.  See the bugzilla ticket for more details.  

Modified:
    struts/shale/trunk/core-library/src/conf/validator-rules.xml
    struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/resources/Bundle.properties
    struts/shale/trunk/core-library/src/java/org/apache/shale/taglib/CommonsValidatorTag.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/validator/CommonsValidator.java
    struts/shale/trunk/core-library/src/test/org/apache/shale/validator/CommonsValidatorTestCase.java
    struts/shale/trunk/core-library/src/test/org/apache/shale/validator/custom-rules.xml

Modified: struts/shale/trunk/core-library/src/conf/validator-rules.xml
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/conf/validator-rules.xml?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/conf/validator-rules.xml (original)
+++ struts/shale/trunk/core-library/src/conf/validator-rules.xml Mon Apr 17 20:21:33 2006
@@ -158,23 +158,227 @@
 			jsFunction="org.apache.commons.validator.javascript.validateEmail"			
 			depends=""/>
 
-        <validator name="url"
-            classname="org.apache.commons.validator.GenericValidator"
-                   method="isUrl"
-                   methodParams="java.lang.String"
-                   depends=""
-                   msg="errors.url"/>
+             <validator name="url"
+                  classname="org.apache.commons.validator.GenericValidator"
+                  method="isUrl"
+                  methodParams="java.lang.String"
+                  depends=""
+                  msg="errors.url"/>
 
 
 
-        <validator name="includeJavaScriptUtilities"
-                   classname=""
-                   method=""
-                   methodParams=""
-                   depends=""
-                   msg=""
-                   jsFunction="org.apache.commons.validator.javascript.validateUtilities"/>
+              <validator name="includeJavaScriptUtilities"
+                  classname=""
+                  method=""
+                  methodParams=""
+                  depends=""
+                  msg=""
+                  jsFunction="org.apache.commons.validator.javascript.validateUtilities"/>
 
 	</global>
+
+   <formset>
+      
+      <form name="org.apache.shale.validator.required">
+          <field property="required">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/> 
+
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.maxlength">
+          <field property="maxlength">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="maxlength" resource="false"/>
+              <arg position="2" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>      
+              <arg position="1" name="parameter" key="maxlength" resource="false"/>   
+
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.minlength">
+          <field property="minlength">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="minlength" resource="false"/>
+              <arg position="2" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>    
+              <arg position="1" name="parameter" key="minlength" resource="false"/> 
+
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.mask">
+          <field property="mask">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="mask" resource="false"/>
+              <arg position="2" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+              <arg position="1" name="parameter" key="mask" resource="false"/>  
+
+              <var>
+                  <var-name>mask</var-name>
+                  <var-value></var-value>
+                  <var-jstype>regexp</var-jstype>
+              </var> 
+
+          </field>      
+      </form>  
+      
+
+      <form name="org.apache.shale.validator.byte">
+          <field property="byte">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+
+              <arg position="0" name="jscallback" key="ByteValidations"/>                            
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.short">
+          <field property="short">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+
+              <arg position="0" name="jscallback" key="ShortValidations"/>                            
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.integer">
+          <field property="integer">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+
+              <arg position="0" name="jscallback" key="IntegerValidations"/>                            
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.long">
+          <field property="long">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+
+          </field>      
+      </form>  
+      
+      <form name="org.apache.shale.validator.float">
+          <field property="float">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+
+              <arg position="0" name="jscallback" key="FloatValidations"/>                            
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.double">
+          <field property="double">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+          </field>      
+      </form>  
+      
+      <form name="org.apache.shale.validator.date">
+          <field property="date">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              <arg position="2" name="message" key="datePatternStrict" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+              <arg position="1" name="parameter" key="datePatternStrict" resource="false"/>
+
+              <arg position="0" name="jscallback" key="DateValidations"/>                                          
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.intRange">
+          <field property="intRange">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="min" resource="false"/>
+              <arg position="2" name="message" key="max" resource="false"/>
+              <arg position="3" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+              <arg position="1" name="parameter" key="min" resource="false"/>  
+              <arg position="2" name="parameter" key="max" resource="false"/>  
+
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.floatRange">
+          <field property="floatRange">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="min" resource="false"/>
+              <arg position="2" name="message" key="max" resource="false"/>
+              <arg position="3" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+              <arg position="1" name="parameter" key="min" resource="false"/>  
+              <arg position="2" name="parameter" key="max" resource="false"/>  
+
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.doubleRange">
+          <field property="doubleRange">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="min" resource="false"/>
+              <arg position="2" name="message" key="max" resource="false"/>
+              <arg position="3" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+              <arg position="1" name="parameter" key="min" resource="false"/>  
+              <arg position="2" name="parameter" key="max" resource="false"/>  
+
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.creditCard">
+          <field property="creditCard">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.email">
+          <field property="email">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+          </field>      
+      </form>  
+
+      <form name="org.apache.shale.validator.url">
+          <field property="url">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>  
+          </field>      
+      </form>  
+
+         
+   </formset>
+
 
 </form-validation>

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java Mon Apr 17 20:21:33 2006
@@ -23,7 +23,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
 
 import javax.faces.component.EditableValueHolder;
 import javax.faces.component.UIComponent;
@@ -34,6 +33,7 @@
 import javax.faces.el.ValueBinding;
 
 import org.apache.commons.validator.ValidatorAction;
+import org.apache.commons.validator.Var;
 import org.apache.shale.renderer.ValidatorInputRenderer;
 import org.apache.shale.validator.CommonsValidator;
 
@@ -67,7 +67,7 @@
 
     /**
      * <p>The component renders itself; therefore, this
-     *    method returns null.
+     *    method returns null.</p>
      */
    public String getRendererType() { 
       return null; 
@@ -77,7 +77,7 @@
     /**
      * <p>Returns the component's family. In this case,
      *    the component is not associated with a family,
-     *    so this method returns null.
+     *    so this method returns null.</p>
      */
    public String getFamily() { 
       return null; 
@@ -179,7 +179,7 @@
    
 
    /**
-     * <p>Registers a validator according to type and id.
+     * <p>Registers a validator according to type and id.</p>
      *
      * @param type The type of the validator
      * @param id The validator's identifier
@@ -272,7 +272,7 @@
 
 
     /**
-     * <p>Write the start of the script for client-side validation.
+     * <p>Write the start of the script for client-side validation.</p>
      *
      * @param writer A response writer
      */
@@ -285,7 +285,7 @@
 
 
     /**
-     * <p>Write the end of the script for client-side validation.
+     * <p>Write the end of the script for client-side validation.</p>
      *
      * @param writer A response writer
      */
@@ -297,7 +297,7 @@
 
     /**
      * <p>Returns the name of the JavaScript function, specified in
-     *    the JSP page (presumably), that validates this JSP page's form.
+     *    the JSP page (presumably), that validates this JSP page's form.</p>
      *
      * @param writer A response writer
      * @param context The FacesContext for this request
@@ -339,18 +339,7 @@
               
               // most of the type the callback function is based on the form name and 
               // type but for some rules require special names
-              String fnameMnemonic = type;
-              if (type.equals("byte")) 
-                 fnameMnemonic = "ByteValidations";
-              else if (type.equals("short")) 
-                 fnameMnemonic = "ShortValidations";
-              else if (type.equals("integer")) 
-                 fnameMnemonic = "IntegerValidations";
-              else if (type.equals("float"))
-                  fnameMnemonic = "FloatValidations";
-              else if (type.equals("date"))
-                  fnameMnemonic = "DateValidations";
-               
+              String fnameMnemonic = CommonsValidator.getJsCallbackMnemonic(type);
               
               callback.append(formName).append('_').append(fnameMnemonic);
               writer.write(callback.toString());
@@ -391,6 +380,49 @@
 
    
    /**
+    * <p>Backslash-escapes the following characters from the input string:
+    * &quot;, &apos;, \, \r, \n.</p>
+    *
+    * <p>This method escapes characters that will result in an invalid
+    * Javascript statement within the validator Javascript.</p>
+    *
+    * @param str The string to escape.
+    * @return The string <code>s</code> with each instance of a double quote,
+    *         single quote, backslash, carriage-return, or line feed escaped
+    *         with a leading backslash.
+    */
+   private String escapeJavascript(String str) {
+       if (str == null) {
+           return null;
+       }
+       
+       int length = str.length();
+       
+       if (length == 0) {
+           return str;
+       }
+       
+       // guess at how many chars we'll be adding...
+       StringBuffer out = new StringBuffer(length + 4);
+       
+       // run through the string escaping sensitive chars
+       for (int i = 0; i < length; i++) {
+           char c = str.charAt(i);
+           
+           if ((c == '"') || (c == '\'') || (c == '\\') || (c == '\n')
+                   || (c == '\r')) {
+               out.append('\\');
+           }
+           
+           out.append(c);
+       }
+       
+       return out.toString();
+   }
+
+   
+   
+   /**
     * <p>Returns an array of validator types organized by dependencies.</p>
     */
    private List getTypesOrderedByDependencies(Set typeSet) {
@@ -429,7 +461,7 @@
 
     /**
      * <p>Writes the JavaScript parameters for the client-side
-     *    validation code.
+     *    validation code.</p>
      *
      * @param writer A response writer
      * @param context The FacesContext for this request
@@ -439,25 +471,46 @@
    public void writeJavaScriptParams(ResponseWriter writer, 
       FacesContext context, String id, CommonsValidator v) throws IOException {       
  
+      ValidatorAction validatorAction = v.getValidatorAction(); 
       writer.write("new Array(\"");
       writer.write(id);
       writer.write("\", \"");
-      writer.write(v.getErrorMessage(v.getArg(), context));
+      writer.write(v.getErrorMessage(context, validatorAction));
       writer.write("\", new Function(\"x\", \"return {");
       
-      String[] names = v.getParamNames();
-      Object[] params = v.getParams();
-
-      for (int i = 0; i < names.length; i++) {
-         if (i > 0) writer.write(",");
-         writer.write(names[i]);
+      Iterator vi = v.getVars().entrySet().iterator();
+     
+      boolean first = true;
+      
+      next: while (vi.hasNext()) {
+         Map.Entry e = (Map.Entry) vi.next();
+          
+         Object value = e.getValue();
+         if (value == null)
+            continue next;
+          
+         String name = (String) e.getKey();
+         if (!first) 
+             writer.write(",");
+         else 
+             first = false;
+         
+         writer.write(name);
          writer.write(":");
+         
+         String jsType = v.getVarType(name);
          // Ugh...mask validator doesn't construct RegExp
-         if (names[i].equals("mask"))
-            writer.write("/"); else writer.write("'");
-         writer.write(params[i].toString());
-         if (names[i].equals("mask")) 
-            writer.write("/"); else writer.write("'");
+         if (jsType.equals(Var.JSTYPE_REGEXP))
+             writer.write("/"); 
+         else 
+             writer.write("'");
+         
+         writer.write(escapeJavascript(value.toString()));
+         
+         if (jsType.equals(Var.JSTYPE_REGEXP)) 
+             writer.write("/"); 
+         else 
+             writer.write("'");
       }
       writer.write("}[x];\"))");
    }
@@ -469,7 +522,7 @@
     * if one is not found.</p>
     *  
     * @param component <code>UIForm</code> parent of the component.
-    * @return
+    * @return client id of the parent form component
     */
    public String findForm(FacesContext context, UIComponent component) {
     
@@ -490,7 +543,7 @@
      * <p>Begin encoding for this component. This method
      *    finds all Commons validators attached to components
      *    in the current component hierarchy and writes out
-     *    JavaScript code to invoke those validators, in turn.
+     *    JavaScript code to invoke those validators, in turn.</p>
      *
      * @param context The FacesContext for this request
      */

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/resources/Bundle.properties
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/resources/Bundle.properties?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/resources/Bundle.properties (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/resources/Bundle.properties Mon Apr 17 20:21:33 2006
@@ -64,3 +64,7 @@
 convHelper.missing=You have requested a converter, but the type specified is null
 convHelper.noConverter=You have requested a conversion for type {0}, but there is no by-type converter registered for this type
 
+commonsValidator.intException=Error initializing commons validator for rule type "{0}" on component id "{1}".
+commonsValidator.loadresource=Loading validation rules file from {0}
+commonsValidator.skipresource=Skipping validation rules file from "{0}".  No url could be located.
+commonsValidator.loaderror=Can't initialize resources
\ No newline at end of file

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/taglib/CommonsValidatorTag.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/taglib/CommonsValidatorTag.java?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/taglib/CommonsValidatorTag.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/taglib/CommonsValidatorTag.java Mon Apr 17 20:21:33 2006
@@ -275,7 +275,7 @@
               iterator.hasNext();) {
             Object key = iterator.next();
             Object value = tagUtils.eval((String) params.get(key));
-            validator.addParam(key, value);
+            validator.getVars().put(key, value);
          }
       }
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/validator/CommonsValidator.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/validator/CommonsValidator.java?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/validator/CommonsValidator.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/validator/CommonsValidator.java Mon Apr 17 20:21:33 2006
@@ -31,13 +31,11 @@
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 import java.util.StringTokenizer;
-import java.util.LinkedHashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.faces.application.Application;
 import javax.faces.application.FacesMessage;
-import javax.faces.component.EditableValueHolder;
 import javax.faces.component.UIComponent;
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
@@ -46,16 +44,110 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.commons.validator.Arg;
+import org.apache.commons.validator.Field;
+import org.apache.commons.validator.Form;
 import org.apache.commons.validator.GenericValidator;
 import org.apache.commons.validator.ValidatorAction;
 import org.apache.commons.validator.ValidatorResources;
-
+import org.apache.commons.validator.Var;
+import org.apache.shale.util.Messages;
 import org.xml.sax.SAXException;
 
 /**
  * <p>This is a JSF validator that uses
  *    a Commons Validator to perform validation, either
- *    client- or server-side.
+ *    client-side or server-side.
+ * </p>
+ * 
+ *  <p>The current implementation is dependent on version 1.3 of commons validator.  
+ * Some new conventions have been adopted for registering a validation rule in 
+ * the validator's XML configuration file.  In the action framework, validation 
+ * was suited for declaring rules associated with a form.  This worked well since 
+ * generally a single action had the responsibility of handling all the posted 
+ * form data at once.</p>  
+ * 
+ * <p>However, JSF takes a component based approach.  Each component has the 
+ * responsibility of managing its posted data and rendering its state.   In the 
+ * component based world, it is easier to associated configuration values at 
+ * that level versus what works best for struts action.</p>
+ * 
+ * <p>In an effort to reuse as much of commons validator and provide a method of 
+ * registering new rules, a new convention was adopted for declaring validation 
+ * rules.
+ * </p>  
+ * 
+ * <pre>
+ *    &lt;global&gt;
+ *      &lt;validator name="mask"
+ *                classname="org.apache.commons.validator.GenericValidator"
+ *                method="matchRegexp"
+ *                methodParams="java.lang.String,java.lang.String"
+ *                msg="errors.invalid"
+ *                jsFunctionName="validateMask"
+ *                jsFunction="org.apache.commons.validator.javascript.validateMask"           
+ *                depends=""/&gt;
+ *    &lt;/global&gt;
+ * </pre>
+ *  
+ * <p>The rules declaration is the same but an added form is required to capture extra 
+ * configuration information.  The form is associated with the validation rule using a 
+ * naming convention.  The prefix of the form name is "org.apache.shale.validator.XXXX" 
+ * where "XXXX" is the validation rule name.</p>
+ * 
+ * <pre>
+ *   &lt;formset&gt;
+ *     &lt;form name="org.apache.shale.validator.mask"&gt;
+ * </pre>
+ *  
+ * 
+ * <p>The form is followed by a field and the property attribute of the form has the 
+ * same value as the rule name.</p>  
+ * 
+ * <pre>
+ *           &lt;field property="mask"&gt;
+ * </pre>
+ * 
+ * <p>Within the field definition, arg's are used to define the parameters in order 
+ * for message substitution and method argument value resolution.  There are two reserved 
+ * name values for the arg node used to define messages and parameters.</p>  
+ * 
+ * <pre>
+ *               &lt;arg position="0" name="message" key="arg" resource="false"/&gt;
+ *               &lt;arg position="1" name="message" key="mask" resource="false"/&gt;
+ *               &lt;arg position="2" name="message" key="submittedValue" resource="false"/&gt;
+ *               
+ *               &lt;arg position="0" name="parameter" key="submittedValue" resource="false"/&gt;  
+ *               &lt;arg position="1" name="parameter" key="mask" resource="false"/&gt; 
+ * </pre> 
+ * 
+ * <p>The "message" name arguments defines the possible <code>MessageFormat</code> parameter substitution  
+ * where the "position" corresponds to the substitution parameter.</p>
+ * 
+ * <pre>
+ *    errors.invalid={0} is invalid.
+ * </pre>
+ * 
+ * <p>The "parameter" arguments define the variable names that hold values for the target validatior method 
+ * identified by the validator rule name.  The comma delimited class types in the "methodParms" value list 
+ * correspond to the parameters by position.</p>
+ * 
+ * <pre>
+ *   methodParams="java.lang.String,java.lang.String"
+ * </pre>
+ * 
+ * <p>The var node is also used to explicitly define a JavaScript variable type.  If not 
+ * defined, the default is "string".  The var-value is ignored because its captured by 
+ * the shale commons validator instance.</p>
+ *  
+ * <pre>
+ *               &lt;var&gt;
+ *                   &lt;var-name&gt;mask&lt;/var-name&gt;
+ *                  &lt;var-value&gt;&lt;/var-value&gt;
+ *                  &lt;var-jstype&gt;regexp&lt;/var-jstype&gt;
+ *               &lt;/var&gt; 
+ * </pre>
+ *    
  * 
  * $Id$
  */
@@ -79,6 +171,13 @@
     
 
     /**
+     * <p>Localized messages for this class.</p>
+     */
+    private static final Messages messages =
+        new Messages("org.apache.shale.resources.Bundle");
+    
+        
+    /**
      * <p>Log instance for this class.</p>
      */
     private static final Log log = LogFactory.getLog(CommonsValidator.class);
@@ -106,28 +205,11 @@
 
     // -------------------------------------------------------- Instance Variables
 
-
     /**
      * <p>Validator type.</p>
      */
     private String type;
-
-
-    /**
-     * <p>The <code>validate</code> method uses this
-     *    as the text of an error message it stores on the
-     *    FacesContext when validation fails.</p>
-     */
-    private String message;
-
-
-    /**
-     * <p>Parameter for the error message. This parameter
-     *    is passed through to the Commons validator.</p>
-     */
-    private String arg;
-
-
+    
     /**
      * <p>Enable client-side validation?</p>
      */
@@ -141,74 +223,36 @@
 
 
     /**
-     * <p>Minimum value.</p>
-     */
-
-    private Double min;
-
-
-    /**
-     * <p>Maximum value.</p>
-     */
-    private Double max;
-
-
-    /**
-     * <p>Minimum length, in characters. Use with type="minlength".</p>
-     */
-    private Integer minlength;
-
-
-    /**
-     * <p>Maximum length, in characters. Use with type="minlength".</p>
-     */
-    private Integer maxlength;
-
-
-    /**
-     * <p>A regular expression that's applied to the value.</p>
-     */
-    private String mask;
-
-
-    /**
-     * <p>Is date validation strict?</p>
+     * <p>The <code>validate</code> method uses this
+     *    as the text of an error message it stores on the
+     *    FacesContext when validation fails.</p>
      */
-    private String datePatternStrict;
+    private String message;
 
 
     /**
      * <p>Parameters for the specific Commons Validator to be used.</p>
      */
-    private LinkedHashMap params;
-
-
-    // -------------------------------------------------------- Transient Variables
-
-
-    /**
-     * <p>A Commons Validator action, that carries out the actual validation.</p>
-     */
-    private transient ValidatorAction validatorAction;
-    
+    private Map vars = new HashMap();
 
     /**
-     * <p>The Commons Validator method.</p>
+     * Returns a <code>Map</code> of variables that can be passed to a 
+     * commons validator method or used to create a parameterized error
+     * message.  Several of the public properties are contained within 
+     * the <code>vars</code> collection.  These include: arg, min, max,
+     * minlength, maxlength, mask, datePatternStrict.</p>
+     *  
+     * @return A value paired collection of variables used to invoke a 
+     * validator method.
      */
-    private transient Method validatorMethod;
+    public Map getVars() {
+       return vars;    
+    }
     
 
-    /**
-     * <p>An array of validator classes.</p>
-     */
-    private transient Class[] paramTypes;
-    
+    // -------------------------------------------------------- Transient Variables
 
-    /**
-     * <p>A Commons validator.</p>
-     */
-    private transient Object validator;
-    
+      
 
     // ----------------------------------------------------- Property Accessors
 
@@ -219,14 +263,14 @@
      *
      * @param newValue The new value for the <code>type</code> property.
      */
-   public void setType(String newValue) { type = newValue; } 
+   public void setType(String newValue) {type = newValue;} 
     
 
     /**
      * <p>The getter method for the <code>type</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public String getType() { return type; }
+   public String getType() {return type;}
     
 
     /**
@@ -235,7 +279,7 @@
      *
      * @param newValue The new value for the <code>client</code> property.
      */
-   public void setClient(Boolean newValue) { client = newValue; } 
+   public void setClient(Boolean newValue) {client = newValue; } 
     
 
     /**
@@ -251,7 +295,7 @@
      *
      * @param newValue The new value for the <code>server</code> property.
      */
-   public void setServer(Boolean newValue) { server = newValue; } 
+   public void setServer(Boolean newValue) {server = newValue; } 
     
 
     /**
@@ -267,7 +311,7 @@
      *
      * @param newValue The new value for the <code>message</code> property.
      */
-   public void setMessage(String newValue) { message = newValue; } 
+   public void setMessage(String newValue) {message = newValue; } 
     
 
     /**
@@ -276,22 +320,25 @@
      */
    public String getMessage() { return message; }
     
-
+   private static final String ARG_VARNAME = "arg";
+   
     /**
      * <p>The setter method for the <code>arg</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      *
      * @param newValue The new value for the <code>arg</code> property.
      */
-   public void setArg(String newValue) { arg = newValue; } 
+   public void setArg(String newValue) { vars.put(ARG_VARNAME, newValue); } 
     
 
     /**
      * <p>The getter method for the <code>arg</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public String getArg() { return arg; }
-    
+   public String getArg() { return (String) vars.get(ARG_VARNAME); }
+
+   
+   private static final String MIN_VARNAME = "min";
 
     /**
      * <p>The setter method for the <code>min</code> property. This property is 
@@ -299,15 +346,16 @@
      *
      * @param newValue The new value for the <code>min</code> property.
      */
-   public void setMin(Double newValue) { min = newValue; } 
+   public void setMin(Double newValue) { vars.put(MIN_VARNAME, newValue); } 
     
 
     /**
      * <p>The getter method for the <code>min</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public Double getMin() { return min; }
+   public Double getMin() { return (Double) vars.get(MIN_VARNAME); }
     
+   private static final String MAX_VARNAME = "max";
 
     /**
      * <p>The setter method for the <code>max</code> property. This property is 
@@ -315,63 +363,69 @@
      *
      * @param newValue The new value for the <code>max</code> property.
      */
-   public void setMax(Double newValue) { max = newValue; } 
+   public void setMax(Double newValue) { vars.put(MAX_VARNAME, newValue); } 
     
 
     /**
      * <p>The getter method for the <code>max</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public Double getMax() { return max; }
+   public Double getMax() { return (Double) vars.get(MAX_VARNAME); }
     
 
+   
+   private static final String MIN_LENGTH_VARNAME = "minlength";
     /**
      * <p>The setter method for the <code>minlength</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      *
      * @param newValue The new value for the <code>minlength</code> property.
      */
-   public void setMinLength(Integer newValue) { minlength = newValue; } 
+   public void setMinLength(Integer newValue) { vars.put(MIN_LENGTH_VARNAME,  newValue); } 
     
 
     /**
      * <p>The getter method for the <code>minLength</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public Integer getMinLength() { return minlength; }
+   public Integer getMinLength() { return (Integer) vars.get(MIN_LENGTH_VARNAME); }
     
 
+   private static final String MAX_LENGTH_VARNAME = "maxlength";
     /**
      * <p>The setter method for the <code>maxlength</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      *
      * @param newValue The new value for the <code>maxlength</code> property.
      */
-   public void setMaxLength(Integer newValue) { maxlength = newValue; } 
+   public void setMaxLength(Integer newValue) { vars.put(MAX_LENGTH_VARNAME, newValue); } 
     
 
     /**
      * <p>The getter method for the <code>maxLength</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public Integer getMaxLength() { return maxlength; }
+   public Integer getMaxLength() { return (Integer) vars.get(MAX_LENGTH_VARNAME); }
     
 
+   private static final String MASK_VARNAME = "mask";
     /**
      * <p>The setter method for the <code>mask</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      *
      * @param newValue The new value for the <code>mask</code> property.
      */
-   public void setMask(String newValue) { mask = newValue; } 
+   public void setMask(String newValue) { vars.put(MASK_VARNAME, newValue); } 
     
 
     /**
      * <p>The getter method for the <code>mask</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public String getMask() { return mask; }
+   public String getMask() { return (String) vars.get(MASK_VARNAME); }
     
+   
+   private static final String DATE_PATTERN_STRICT_VARNAME = "datePatternStrict";
 
     /**
      * <p>The setter method for the <code>datePatternStrict</code> property. This property is 
@@ -379,9 +433,8 @@
      *
      * @param newValue The new value for the <code>datePatternStrict</code> property.
      */
-   public void setDatePatternStrict(String newValue) { 
-       
-      datePatternStrict = newValue;
+   public void setDatePatternStrict(String newValue) {     
+      vars.put(DATE_PATTERN_STRICT_VARNAME, newValue);
    }
 
 
@@ -389,236 +442,449 @@
      * <p>The getter method for the <code>datePatternStrict</code> property. This property is 
      *    passed through to the Commons Validator.</p>
      */
-   public String getDatePatternStrict() { return datePatternStrict; }
+   public String getDatePatternStrict() { return (String) vars.get(DATE_PATTERN_STRICT_VARNAME); }
     
 
+
    /**
-    * <p>Add a parameter for the specific validator being used.</p>
-    *
-    * @param key     The name of the validator parameter to add.
-    * @param value   The value of the validator parameter.
+    * This method lazily configures validator resources by reading either
+    * the default <code>validalidator-rules.xml</code> file in 
+    * shale-core.jar or the list of resources configured using the init 
+    * param <code>org.apache.shale.validator.VALIDATOR_RULES</code>.
     */
-   public void addParam(Object key, Object value) {
-      if (params == null) {
-         params = new LinkedHashMap();
-      }
-      params.put(key, value);
-   }
+    private static ValidatorResources getValidatorResources() {
+        final String VALIDATOR_RESOURCES_KEY = 
+            "org.apache.shale.validator.resources";
+       FacesContext context = FacesContext.getCurrentInstance();
+       ExternalContext external = context.getExternalContext();
+       Map applicationMap = external.getApplicationMap();
+       ValidatorResources validatorResources 
+          = (ValidatorResources) applicationMap.get(VALIDATOR_RESOURCES_KEY);
+       if (validatorResources == null) { 
+          try {
+             String pathnames = external.getInitParameter(Globals.VALIDATOR_RULES);
+             if (pathnames == null || pathnames.length() <= 0) {
+                pathnames = Globals.DEFAULT_VALIDATOR_RULES;
+             }
+             StringTokenizer st = new StringTokenizer(pathnames, ",");
+             List urlList = new ArrayList();
+             while (st.hasMoreTokens()) {
+                String validatorRules = st.nextToken().trim();
+                logger.log(Level.INFO, messages.getMessage("commonsValidator.loadresource", new Object[] {validatorRules}));
+                URL input = external.getResource(validatorRules);
+                if (input == null) {
+                   input = CommonsValidator.class.getResource(validatorRules);
+                }
+                if (input != null) {
+                   urlList.add(input);
+                } else {
+                   logger.log(Level.WARNING, messages.getMessage("commonsValidator.skipresource", new Object[] {validatorRules}));
+                }
+             }
+             int urlSize = urlList.size();
+             String[] urlArray = new String[urlSize];
+             for (int urlIndex = 0; urlIndex < urlSize; urlIndex++) {
+                URL url = (URL) urlList.get(urlIndex);
+                urlArray[urlIndex] = url.toExternalForm();
+             }
+             validatorResources = new ValidatorResources(urlArray);
+             applicationMap.put(VALIDATOR_RESOURCES_KEY, validatorResources);
+          } catch (IOException ex) {
+             logger.log(Level.SEVERE, messages.getMessage("commonsValidator.loaderror"), ex);
+             return null;
+          } catch (SAXException ex) {
+             logger.log(Level.SEVERE, messages.getMessage("commonsValidator.loaderror"), ex);
+             return null;
+          }
+       }
+ 
+       return validatorResources; 
+    }
+   
+    
+    private static final String MESSAGE_ARG_NAME = "message";
+    
+    /**
+     * <p>Returns an array of values for message parameter replacement
+     * arguments.  The list and ordering is determined by a form 
+     * registered in the common validators XML.  The form name
+     * and the fields property is tied to the validation rule name
+     * by convention.  The the <cod>arg</code> name attribute is
+     * assumed to be "message" for message argument grouping.</p>
+     *  
+     * @param ruleName name of the validation rule
+     * @return array of objects used to fill the message
+     */
+    protected Object[] getMessageArgs(String ruleName) {
+      
+       Arg[] templateArgs = getArgs(MESSAGE_ARG_NAME, ruleName);
+       assert templateArgs != null;
+       
+       Object[] target = new Object[templateArgs.length];
+       
+       for (int i = 0; i < templateArgs.length; i++) {
+          target[i] = vars.get(templateArgs[i].getKey());    
+       }
+       
+       return target;   
+    }
+  
+    /**
+     * <p>Returns an array of class types corresponding to the the 
+     * target validation rules method signature.  The params are 
+     * configured by the <code>validator</code>'s <code>methodParams</code>
+     * attribute.</p>
+     *  
+     * @param validationAction the validators configuration bean populated from the XML file.
+     * @return an array of class types for the formal parameter list.
+     * @throws ClassNotFoundException
+     */
+    protected Class[] loadMethodParamClasses(ValidatorAction validationAction) throws ClassNotFoundException {
+        
+        List tmp = new ArrayList();       
+        StringTokenizer tokenizer = new StringTokenizer(validationAction.getMethodParams(), ",");        
+        while (tokenizer.hasMoreTokens()) { 
+            String token = tokenizer.nextToken().trim(); 
+            if (token.length() > 0)
+                tmp.add(token);
+        }
+        
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader == null) {
+            classLoader = this.getClass().getClassLoader();
+        }
+        assert classLoader != null;
+        
+        
+        Class[] parameterClasses = new Class[tmp.size()];
+        for (int i = 0; i < tmp.size(); i++) {
+            String className = (String) tmp.get(i);
+            if (standardTypes.containsKey(className))
+                parameterClasses[i] = (Class) standardTypes.get(className);
+            else
+                parameterClasses[i] = classLoader.loadClass(className);
+        }
+        
+        return parameterClasses;
+    }    
+     
+    
+    private static final String PARAMETER_ARG_NAME = "parameter";
+
+    /**
+     * <p>Returns an array of parameter names in the target validator's 
+     * method.  The parameter names are defined in the validators configuration
+     * file under a form and field that correspond to the target rule.
+     * The name attribute of the nested <code>arg</code> is assumed to be
+     * "parameter".
+     * 
+     * @param name the name of the target validation rule 
+     * @return array of formal parameter names
+     */
+    public String[] getMethodParamNames(String name) {
+        Arg[] templateArgs = getArgs(PARAMETER_ARG_NAME, name);
+        assert templateArgs != null;
+                
+        String[] target = new String[templateArgs.length];
+            
+        for (int i = 0; i < templateArgs.length; i++) {
+           target[i] = templateArgs[i].getKey();    
+        }
+        
+        return target;         
+    }
 
+    private static final String FORM_NAME_PREFIX = "org.apache.shale.validator.";
+    
+    /**
+     * <p>Returns validator <code>Arg</code> beans from the configuration file.
+     * The <code>form</code> and <code>field</code> nodes that contain the target
+     * arguments have a naming convention to a validation rule.  This convention
+     * was adopted for JSF validators because the <code>var</code>'s of the defining 
+     * validator is defined by the JSF validator object and not in the config file.
+     * The JSF implementation doesn't have a concept of defining global form field
+     * characteristics outside of the associated JSF component.  But, we needed 
+     * a place to explicitly declare parameter names and message arguments.<p>
+     *  
+     * @param name the name of the <code>arg</code> name attribute.
+     * @param ruleName the name of the validator rule
+     * @return an array of validator <code>Arg</code> beans.
+     */
+    protected static Arg[] getArgs(String name, String ruleName) {
+             
+        StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX);
+        formName.append(ruleName);
+        
+        Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString());
+        assert formDef != null; 
+        
+        Field field = formDef.getField(ruleName);
+        assert field != null;
+        
+        Arg[] templateArgs = field.getArgs(name);
+        
+        int max = -1;
+        for (int i = templateArgs.length - 1; i > -1; i--) {
+           if (templateArgs[i] != null) { 
+              max = i;
+              break;
+           }
+        }
+        if (max == -1)
+           return new Arg[0];
+        else if (max < templateArgs.length - 1) {
+           Arg[] tmp = new Arg[max + 1];
+           System.arraycopy(templateArgs, 0, tmp, 0, max + 1);
+           templateArgs = tmp;
+        }
+        
 
+        return templateArgs;  
+    }
+    
+    
     /**
-     * <p>The <code>Object[]</code> returned from this method represents 
-     *    parameters that were explicitly set for this validator. 
-     *    Typically, that happens in the <code>s:validatorVar</code>
-     *    tag or the <code>s:commonsValidator</code> tag.
-     */
-   public Object[] getParams() {
-      // if the params map is empty, try the individual properties
-      if ((params == null) || (params.size() == 0)) {
-         ArrayList r = new ArrayList();
-         if (min != null) r.add(min);
-         if (max != null) r.add(max);
-         if (minlength != null) r.add(minlength);
-         if (maxlength != null) r.add(maxlength);
-         if (mask != null) r.add(mask);
-         if (datePatternStrict != null) r.add(datePatternStrict);
-         return r.toArray();
-      }
-      return params.values().toArray();
-   }
+     * <p>Returns the JavaScript type for a <code>var</code> collection
+     * item.  The default is <code>Var.JSTYPE_STRING</code>.  The type
+     * can be overridden by adding a <code>var</code> node to the 
+     * <code>field</code> node in the validator configuration file.
+     * The <code>var-value</code> is ignored but a value is required 
+     * for well-formness.  The <code>var-jstype</code>
+     * and <code>var-name</code> are the only values used. 
+     * The <code>form</code> and the <code>field</code> the <code>var</code>
+     * is nested under has an association with the <code>type</code>.
+     * </p>
+     * @param varName The name of the target variable
+     * @return The JavaScript variable type ("string", "int", "regexp")
+     */
+    public String getVarType(String varName) {
+        StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX);
+        formName.append(getType());
+        
+        Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString());
+        assert formDef != null; 
+        
+        Field field = formDef.getField(getType());
+        assert field != null;
+
+        String jsType = Var.JSTYPE_STRING; // default type
+        Var var = field.getVar(varName);
+        
+        if (var != null && var.getJsType() != null) {
+           jsType = var.getJsType();
+        }
+        
+        return jsType;    
+    }
     
+   
+    private static final String JSCALLBACK_ARG_NAME = "jscallback";
 
+    
     /**
-     * <p>The <code>String[]</code> returned from this method represents 
-     *    the names of parameters that were explicitly set for this validator. 
-     *    Typically, that happens in the <code>s:validatorVar</code>
-     *    tag or the <code>s:commonsValidator</code> tag.
-     */
-   public String[] getParamNames() {
-       // if the params map is empty, try the individual properties
-      if ((params == null) || (params.size() == 0)) {
-         ArrayList r = new ArrayList();
-         if (min != null) r.add("min");
-         if (max != null) r.add("max");
-         if (minlength != null) r.add("minlength");
-         if (maxlength != null) r.add("maxlength");
-         if (mask != null) r.add("mask");
-         if (datePatternStrict != null) r.add("datePatternStrict");
-         
-         return (String[]) r.toArray(new String[r.size()]);
-      }
-      return (String[]) params.keySet()
-            .toArray(new String[params.keySet().size()]);
-   }
-
-
+     * <p>Returns a mnemonic used to build the commons validator
+     * javascript call back.  This method is invoked from the
+     * {@link org.apache.shale.component.ValidatorScript} component.
+     * The routine looks for an <code>arg</code> with a name of
+     * <code>jscallback</code> under a form name and field 
+     * property corresponding to the rule.  If there is not
+     * a matching arg found, the <code>ruleName</code> is
+     * returned as the default.
+     * </p>
+     * @param ruleName name of the target rule to invoke
+     * @return code used to create the javacript call back function
+     */
+    public static String getJsCallbackMnemonic(String ruleName) {
+        Arg[] args = getArgs(JSCALLBACK_ARG_NAME, ruleName);
+        if (args == null || args.length == 0)
+           return ruleName;
+        else 
+           return args[0].getKey();
+    }
+    
+    /**
+     * <p>Loads an array of method parameter values corresponding to the 
+     * formal parameter of the target validator's method.<p>
+     * 
+     * @param validatorAction <code>ValidatorAction</code> configuration bean.
+     * @param methodParamClasses <code>Class[]</code> of the parameters of the target method.
+     * @return An array of object valuse for each method parameter.
+     */
+    protected Object[] loadMethodParamValues(ValidatorAction validatorAction, Class[] methodParamClasses) {
+                        
+        String[] paramNames = getMethodParamNames(validatorAction.getName());        
+        assert paramNames != null;
+                
+        Object[] target = new Object[paramNames.length];
+        assert paramNames.length == methodParamClasses.length;
+            
+        for (int i = 0; i < paramNames.length; i++) {
+           Object obj = vars.get(paramNames[i]);
+           target[i] = convert(obj, methodParamClasses[i]);    
+        }
+        
+        return target;   
+     }
+    
+    
+    
+   
     /**
      * <p>Returns the Commons validator action that's appropriate
-     *    for the validator with the given <code>name</code>. This method 
-     *    lazily configures validator resources by reading either
-     *    the default <code>validalidator-rules.xml</code> file in 
-     *    shale-core.jar or the list of resources configured using the init 
-     *    param <code>org.apache.shale.validator.VALIDATOR_RULES</code>.</p>
+     *    for the validator with the given <code>name</code>.</p>
      *
      * @param name The name of the validator
      */
    public static ValidatorAction getValidatorAction(String name) {
-      final String VALIDATOR_RESOURCES_KEY = 
-           "org.apache.shale.validator.resources";
-      FacesContext context = FacesContext.getCurrentInstance();
-      ExternalContext external = context.getExternalContext();
-      Map applicationMap = external.getApplicationMap();
-      ValidatorResources validatorResources 
-         = (ValidatorResources) applicationMap.get(VALIDATOR_RESOURCES_KEY);
-      if (validatorResources == null) { 
-         try {
-            String pathnames = external.getInitParameter(Globals.VALIDATOR_RULES);
-            if (pathnames == null || pathnames.length() <= 0) {
-               pathnames = Globals.DEFAULT_VALIDATOR_RULES;
-            }
-            StringTokenizer st = new StringTokenizer(pathnames, ",");
-            List urlList = new ArrayList();
-            while (st.hasMoreTokens()) {
-               String validatorRules = st.nextToken().trim();
-               logger.log(Level.INFO, "Loading validation rules file from '"
-                       + validatorRules + "'");
-               URL input = external.getResource(validatorRules);
-               if (input == null) {
-                  input = CommonsValidator.class.getResource(validatorRules);
-               }
-               if (input != null) {
-                  urlList.add(input);
-               } else {
-                  logger.log(Level.WARNING, "Skipping validation rules file from '"
-                          + validatorRules + "'.  No url could be located.");
-               }
-            }
-            int urlSize = urlList.size();
-            String[] urlArray = new String[urlSize];
-            for (int urlIndex = 0; urlIndex < urlSize; urlIndex++) {
-               URL url = (URL) urlList.get(urlIndex);
-               urlArray[urlIndex] = url.toExternalForm();
-            }
-            validatorResources = new ValidatorResources(urlArray);
-            applicationMap.put(VALIDATOR_RESOURCES_KEY, validatorResources);
-         } catch (IOException ex) {
-            logger.log(Level.SEVERE, "can't initialize resources", ex);
-            return null;
-         } catch (SAXException ex) {
-            logger.log(Level.SEVERE, "can't initialize resources", ex);
-            return null;
-         }
-      }
-      return validatorResources.getValidatorAction(name);
+      return getValidatorResources().getValidatorAction(name);
+   }
+   /**
+    * <p>Returns the commons validator action associated with
+    * the <code>type</code> attribute.</p>
+    * 
+    * @return Validator rules config bean.
+    */
+   public ValidatorAction getValidatorAction() {
+       return getValidatorResources().getValidatorAction(getType());
    }
 
+   /**
+    * <p>For a given commons validator rule, returns an array of 
+    * rule names that are dependent of the <code>name</code>.  
+    * Rule dependencies will be first in the returned array.
+    * </p>
+    * @param name target validator rule
+    * @return array of all dependent rules for the target name
+    */
+   protected String[] getDependencies(String name) {
+       
+       ValidatorAction action = getValidatorAction(name);
+       assert action != null;
+       
+       List dependencies = action.getDependencyList();
+       String[] types = new String[dependencies.size() + 1];
+       
+       for (int i = 0; i < dependencies.size(); i++) 
+           types[i] = (String) dependencies.get(i);  
 
-    /**
-     * <p>Returns the Commons validator action that's appropriate
-     *    for this validator. 
-     */
-   public ValidatorAction getValidatorAction() {
-      if (validatorAction == null) validatorAction = getValidatorAction(type);
-      return validatorAction;
+       types[types.length - 1] = name; 
+       
+       return types;
    }
 
+   private static final String SUBMITTED_VALUE_VARNAME = "submittedValue";
 
     /**
      * <p>This <code>validate</code> method is called by JSF to verify
-     *    the component to which the validator is attached.
+     *    the component to which the validator is attached.</p>
      *
      * @param context The faces context
      * @param component The component to validate
+     * @param value the component's submitted value after the converter applied.
      */
-   public void validate(FacesContext context, UIComponent component, 
-      Object value) {
-      if (Boolean.FALSE.equals(server)) return;
-      initValidation();     
-      Object[] p = getParams();
-      Object[] params = new Object[p.length + 1];
-
-      params[0] = convert(value, paramTypes[0], component);
-      for (int i = 1; i < params.length; i++) 
-         params[i] = convert(p[i - 1], paramTypes[i], null);
-
-      try {
-         Boolean r = (Boolean) validatorMethod.invoke(validator, params);
-         
-         if (r.equals(Boolean.FALSE)) {
-            Object errorValue = value;
-
-            if(component instanceof EditableValueHolder) {
-                errorValue = arg;    
-                //errorValue = ((EditableValueHolder)component)
-                //                              .getSubmittedValue();
-            }
-
-            throw new ValidatorException(new FacesMessage(
-                     FacesMessage.SEVERITY_ERROR, 
-                     getErrorMessage(errorValue, context), null));
-         }
-      } catch (IllegalAccessException ex) {
-         logger.log(Level.SEVERE, "can't invoke validator", ex);
-      } catch (InvocationTargetException ex) {
-         logger.log(Level.SEVERE, "can't invoke validator", ex);
-      }
+   public void validate(FacesContext context, UIComponent component, Object value) {
+       
+       if (Boolean.FALSE.equals(getServer()))
+           return;
+       
+       String[] types = getDependencies(getType());
+       for (int j = 0; j < types.length; j++) {
+           ValidatorAction validatorAction = this.getValidatorAction(types[j]);
+           
+           try {
+               vars.put(SUBMITTED_VALUE_VARNAME, value);
+               Class validatorClass = loadValidatorClass(validatorAction);
+               Class[] paramClasses = this.loadMethodParamClasses(validatorAction);
+               Object[] paramValues = this.loadMethodParamValues(validatorAction, paramClasses);
+               Method validatorMethod = this.loadValidatorMethod(validatorAction, validatorClass,  paramClasses);
+               Object validator = null;
+               if (!Modifier.isStatic(validatorMethod.getModifiers())) 
+                   validator = validatorClass.newInstance();
+               Boolean r = (Boolean) validatorMethod.invoke(validatorClass, paramValues);
+               if (r.equals(Boolean.FALSE)) {
+                   throw new ValidatorException(new FacesMessage(
+                           FacesMessage.SEVERITY_ERROR, 
+                           getErrorMessage(context, validatorAction), null));
+               }
+           } catch (IllegalArgumentException e) {
+               throw new RuntimeException(messages.getMessage("commonsValidator.intException", 
+                       new Object[] {getType(), component.getId()}), e);
+           } catch (ClassNotFoundException e) {
+               throw new RuntimeException(messages.getMessage("commonsValidator.intException", 
+                       new Object[] {getType(), component.getId()}), e);
+           } catch (InstantiationException e) {
+               throw new RuntimeException(messages.getMessage("commonsValidator.intException", 
+                       new Object[] {getType(), component.getId()}), e);
+           } catch (IllegalAccessException e) {
+               throw new RuntimeException(messages.getMessage("commonsValidator.intException", 
+                       new Object[] {getType(), component.getId()}), e);
+           } catch (NoSuchMethodException e) {
+               throw new RuntimeException(messages.getMessage("commonsValidator.intException", 
+                       new Object[] {getType(), component.getId()}), e);
+           } catch (InvocationTargetException e) {
+               throw new RuntimeException(messages.getMessage("commonsValidator.intException", 
+                       new Object[] {getType(), component.getId()}), e);
+           } finally {
+               vars.remove(SUBMITTED_VALUE_VARNAME);   
+           }
+           
+       }
    }
 
 
-    /**
-     * <p>Initialize validation by invoking <code>getValidatorAction</code>
-     *    and setting up instance data.
-     */
-   public void initValidation() {
-      if (validatorMethod != null) return;     
-      getValidatorAction();      
-      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-      if (classLoader == null) classLoader = getClass().getClassLoader();
-      try {
-         String methodParams = validatorAction.getMethodParams();
-         // methodParams is a comma separated list of the fully qualified
-         // class names of the method parameters in the correct order
-         StringTokenizer st = new StringTokenizer(methodParams, ",");
-         List params = new ArrayList();
-         while (st.hasMoreTokens()) {
-             String value = st.nextToken().trim();
-             if (value != null && value.length() > 0) {
-                 params.add(value);
-             }
-         }
-         paramTypes = new Class[params.size()];
-         for (int i = 0; i < paramTypes.length; i++) {
-            String paramTypeName = (String) params.get(i);
-            paramTypes[i] = (Class) standardTypes.get(paramTypeName);
-            if (paramTypes[i] == null) 
-               paramTypes[i] = classLoader.loadClass(paramTypeName);
-         }      
-         Class c = classLoader.loadClass(validatorAction.getClassname());      
-         validatorMethod 
-            = c.getMethod(validatorAction.getMethod(), paramTypes);
-         if (!Modifier.isStatic(validatorMethod.getModifiers())) 
-            validator = c.newInstance();
-      } catch (ClassNotFoundException ex) {
-         logger.log(Level.SEVERE, "can't load validator or param class", ex);
-      } catch (NoSuchMethodException ex) {
-         logger.log(Level.SEVERE, "can't get validator method", ex);
-      } catch (InstantiationException ex) {
-         logger.log(Level.SEVERE, "can't instantiate validator", ex);
-      } catch (IllegalAccessException ex) {
-         logger.log(Level.SEVERE, "can't instantiate validator", ex);
-      }
+   /**
+    * <p>Loads the commons validator class containing the target rule.</p>
+    * @param validatorAction the validator rules config bean
+    * @return the class having the target validation method.</p>
+    * 
+    * @throws ClassNotFoundException
+    * @throws InstantiationException
+    * @throws IllegalAccessException
+    */
+   protected Class loadValidatorClass(ValidatorAction validatorAction) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+       
+       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+       if (classLoader == null) {
+           classLoader = this.getClass().getClassLoader();
+       }
+       assert classLoader != null;
+       
+       //find the target class having the validator rule
+       Class validatorClass = classLoader.loadClass(validatorAction.getClassname());
+       
+       return validatorClass;
    }
-
+  
+   /**
+    * <p>Loads the <code>Method</code> of the <code>validatorClass</code> having
+    * using definitions from the <code>validatorAction</code> bean.
+    * </p>
+    * @param validatorAction The config info bean of the target rule.
+    * @param validatorClass The class having the validation method.
+    * @param methodParamClasses The method formal parameter class signature.
+    * @return target commons validator method to invoke.
+    * @throws NoSuchMethodException
+    */
+   protected Method loadValidatorMethod(ValidatorAction validatorAction, Class validatorClass, Class[] methodParamClasses) throws NoSuchMethodException {
+       //find the method on the target validator rule class
+       Method validatorMethod = validatorClass.getMethod(validatorAction.getMethod(), methodParamClasses);      
+       return validatorMethod;
+   }
+   
+   
 
     /**
      * <p>Retrieves an error message, using the validator's message combined
-     *    with the errant value.
+     *    with the errant value.</p>
+     * @param context the all knowing faces conext object.
+     * @param validatorAction config bean defining the commons validator rule. 
+     * @return the message after parameter substitution. 
      */
-   public String getErrorMessage(Object value, FacesContext context) {
+   public String getErrorMessage(FacesContext context, ValidatorAction validatorAction) {
       final String DEFAULT_BUNDLE_NAME = "org.apache.shale.validator.messages";
-
+      
       Locale locale = context.getViewRoot().getLocale();
-      String msg = message;
+      String msg = getMessage();
       if (msg == null) { 
          String msgkey = validatorAction.getMsg();
          ClassLoader loader = Thread.currentThread().getContextClassLoader();
@@ -646,11 +912,7 @@
                }
          }
       }
-      Object[] p = getParams();
-      Object[] params = new Object[p.length + 1];
-      params[0] = value;
-      for (int i = 1; i < params.length; i++) 
-         params[i] = p[i - 1];
+      Object[] params = getMessageArgs(validatorAction.getName());
       msg = new MessageFormat(msg, locale).format(params);
       return msg;
    }
@@ -661,22 +923,20 @@
     /**
      * <p>A utility method that converts an object to an instance
      *    of a given class, such as converting <code>"true"</code> 
-     *    for example, into <code>Boolean.TRUE</code>.
+     *    for example, into <code>Boolean.TRUE</code>.</p>
      * <p>If the component passed to this method is an instance of
      *    <code>EditableValueHolder</code> and the object's class is
      *    <code>String</code>, this method returns the component's
      *     submitted value, without converting it to a string. The
      *     <code>component</code> parameter can be <code>null</code>.
+     *  </p>
      *
      * @param obj The object to convert
      * @param cl The type of object to convert to
-     * @param component The component whose value we are converting
      */
-   private static Object convert(Object obj, Class cl, UIComponent component) {
+   private static Object convert(Object obj, Class cl) {
       if (cl.isInstance(obj)) return obj;
-      if (cl == String.class && component != null && 
-         component instanceof EditableValueHolder)
-         return ((EditableValueHolder)component).getSubmittedValue();
+
       if (cl == String.class) return "" + obj;
       if (obj instanceof String) {
          String str = (String) obj;
@@ -704,6 +964,7 @@
     /**
      * <p>A utility method that returns <code>true</code> if
      *    the supplied string has a length greater than zero.
+     * </p>
      *
      * @param str The string
      */
@@ -715,7 +976,7 @@
 
     /**
      * <p>A utility method that returns <code>true</code> if
-     *    the supplied string represents a date.
+     *    the supplied string represents a date.</p>
      *
      * @param d The string representation of the date.
      * @param datePatternStrict Commons validator property

Modified: struts/shale/trunk/core-library/src/test/org/apache/shale/validator/CommonsValidatorTestCase.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/validator/CommonsValidatorTestCase.java?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/test/org/apache/shale/validator/CommonsValidatorTestCase.java (original)
+++ struts/shale/trunk/core-library/src/test/org/apache/shale/validator/CommonsValidatorTestCase.java Mon Apr 17 20:21:33 2006
@@ -18,10 +18,10 @@
 
 import java.io.StringWriter;
 import java.util.Iterator;
+import java.util.StringTokenizer;
 
 import javax.faces.application.FacesMessage;
 import javax.faces.component.UIComponent;
-import javax.faces.component.UIForm;
 import javax.faces.component.UIInput;
 import javax.faces.component.UIViewRoot;
 import javax.faces.component.html.HtmlForm;
@@ -152,16 +152,31 @@
         else
            return false;
     }
-        
+
+    //test validation rule
+    public static boolean isValid(String value, String colors) {
+       StringTokenizer tokenizer = new StringTokenizer(colors, ",");
+       
+       while (tokenizer.hasMoreTokens()) {
+          String color = tokenizer.nextToken().trim();
+          if (value.equals(color))
+             return true;
+       }
+    
+       return false;
+    }
+
+    
+    
     // Test access to a custom validation rule
-    public void testCustom() {
+    public void testCustom1() {
 
         application.setMessageBundle("org.apache.shale.validator.messages");
 
         
-        ValidatorAction va = CommonsValidator.getValidatorAction("testRule");
+        ValidatorAction va = CommonsValidator.getValidatorAction("testRule1");
         assertNotNull(va);
-        assertEquals("testRule", va.getName());
+        assertEquals("testRule1", va.getName());
         assertEquals("org.apache.shale.validator.CommonsValidatorTestCase",
                 va.getClassname());
         assertEquals("isValid", va.getMethod());
@@ -175,25 +190,67 @@
         UIComponent form1 = this.createForm("test1", root);
          
         //create a dummy component 1
-        HtmlInputText component1 = createInputText("testRule", form1);
+        HtmlInputText component1 = createInputText("testRule1", form1);
         component1.setRequired(true);
                
         //create a required field/server rule 
-        CommonsValidator validator1 = createValidator(component1, "testRule");
+        CommonsValidator validator1 = createValidator(component1, "testRule1");
         
         //set the value
         component1.setSubmittedValue("blue");
         
         // invoke component validation
         component1.validate(facesContext);
+            
+        // check for a error message
+        checkMessages(component1);
+       
+    }
+
+    
+    // Test access to a custom validation rule
+    public void testCustom2() {
+       
+        ValidatorAction va = CommonsValidator.getValidatorAction("testRule2");
+        assertNotNull(va);
+        assertEquals("testRule2", va.getName());
+        assertEquals("org.apache.shale.validator.CommonsValidatorTestCase",
+                va.getClassname());
+        assertEquals("isValid", va.getMethod());
+        
+        
+        // find the view root
+        UIViewRoot root = facesContext.getViewRoot();
+        assertNotNull(root);
+        
+        //create a form 1
+        UIComponent form1 = this.createForm("test1", root);
+         
+        //create a dummy component 1
+        HtmlInputText component1 = createInputText("testRule2", form1);
+        component1.setRequired(true);
+               
+        //create a required field/server rule 
+        CommonsValidator validator1 = createValidator(component1, "testRule2");
+
+        // <s:commonsValidator type="testRule2" message="{0} must be one of the following: {2}." arg="Favorite Color">
+        //    <s:validatorVar name="enumerations" value="black, yellow, red"/>
+        // </s:commonsValidator>
+        validator1.getVars().put("enumerations", "black, yellow, red");
+        validator1.setMessage("{0} must be one of the following: {2}.");
+        validator1.setArg("Favorite Color");
+        
+        //set the value
+        component1.setSubmittedValue("blue");
         
         // invoke component validation
         component1.validate(facesContext);
-    
+            
         // check for a error message
-        checkMessages(component1);
+        checkMessage("Favorite Color must be one of the following: black, yellow, red.", component1);
        
     }
+
     
     // test the required rule with two forms
     public void testValidateRequired() throws Exception {
@@ -366,7 +423,7 @@
         //look for all search tokens
         checkScript(htmlSnippet, searchTokens);
         
-        System.out.println(htmlSnippet.toString());
+        //System.out.println(htmlSnippet.toString());
     }
 
     

Modified: struts/shale/trunk/core-library/src/test/org/apache/shale/validator/custom-rules.xml
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/validator/custom-rules.xml?rev=394829&r1=394828&r2=394829&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/test/org/apache/shale/validator/custom-rules.xml (original)
+++ struts/shale/trunk/core-library/src/test/org/apache/shale/validator/custom-rules.xml Mon Apr 17 20:21:33 2006
@@ -1,17 +1,46 @@
 <!DOCTYPE form-validation PUBLIC
           "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.2.0//EN"
-          "http://jakarta.apache.org/commons/dtds/validator_1_2_0.dtd">
+          "http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd">
 <form-validation>
 
    <global>
-
-       <validator name="testRule"
+       <validator name="testRule1"
              classname="org.apache.shale.validator.CommonsValidatorTestCase"
              method="isValid"
              methodParams="java.lang.String"
              msg="errors.invalid">
        </validator>
 
+       <validator name="testRule2"
+             classname="org.apache.shale.validator.CommonsValidatorTestCase"
+             method="isValid"
+             methodParams="java.lang.String, java.lang.String"
+             msg="errors.invalid">
+       </validator>
+
+
    </global>
+
+   <formset>
+      <form name="org.apache.shale.validator.testRule1">
+          <field property="testRule1">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/>         
+          </field>      
+      </form>  
+      <form name="org.apache.shale.validator.testRule2">
+          <field property="testRule2">
+              <arg position="0" name="message" key="arg" resource="false"/>
+              <arg position="1" name="message" key="submittedValue" resource="false"/>
+              <arg position="2" name="message" key="enumerations" resource="false"/>
+              
+              <arg position="0" name="parameter" key="submittedValue" resource="false"/> 
+              <arg position="1" name="parameter" key="enumerations" resource="false"/>           
+          </field>      
+      </form>  
+               
+   </formset>
 
 </form-validation>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Mime
View raw message