2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.validator;
19  
20  import java.io.Serializable;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  import java.text.MessageFormat;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Locale;
29  import java.util.Map;
30  import java.util.MissingResourceException;
31  import java.util.ResourceBundle;
32  import java.util.StringTokenizer;
33  
34  import javax.faces.application.Application;
35  import javax.faces.application.FacesMessage;
36  import javax.faces.component.EditableValueHolder;
37  import javax.faces.component.UIComponent;
38  import javax.faces.context.ExternalContext;
39  import javax.faces.context.FacesContext;
40  import javax.faces.validator.Validator;
41  import javax.faces.validator.ValidatorException;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  import org.apache.commons.validator.Arg;
46  import org.apache.commons.validator.Field;
47  import org.apache.commons.validator.Form;
48  import org.apache.commons.validator.GenericValidator;
49  import org.apache.commons.validator.ValidatorAction;
50  import org.apache.commons.validator.ValidatorResources;
51  import org.apache.commons.validator.Var;
52  import org.apache.shale.util.ConverterHelper;
53  import org.apache.shale.util.Messages;
54  import org.apache.shale.util.Tags;
55  import org.apache.shale.validator.faces.ValidatorInputRenderer;
56  
57  /***
58   * <p>This is a JavaServer Faces <code>Validator</code> that uses
59   * Jakarta Commons Validator to perform validation, either on the
60   * client side, the server side, or both.</p>
61   *
62   *  <p>The current implementation is dependent on version 1.3 of Commons Validator.
63   * Some new conventions have been adopted for registering a validation rule in
64   * the validator's XML configuration file.  In the action framework, validation
65   * was suited for declaring rules associated with a form.  This worked well since
66   * generally a single action had the responsibility of handling all the posted
67   * form data at once.</p>
68   *
69   * <p>However, JSF takes a component based approach.  Each component has the
70   * responsibility of managing its posted data and rendering its state.   In the
71   * component based world, it is easier to associate configuration values at
72   * that level versus what works best for Struts actions.</p>
73   *
74   * <p>In an effort to reuse as much of Commons Validator and provide a method of
75   * registering new rules, a new convention was adopted for declaring validation
76   * rules.</p>
77   *
78   * <pre>
79   *    &lt;global&gt;
80   *      &lt;validator name="mask"
81   *                classname="org.apache.commons.validator.GenericValidator"
82   *                method="matchRegexp"
83   *                methodParams="java.lang.String,java.lang.String"
84   *                msg="errors.invalid"
85   *                jsFunctionName="validateMask"
86   *                jsFunction="org.apache.commons.validator.javascript.validateMask"
87   *                depends=""/&gt;
88   *    &lt;/global&gt;
89   * </pre>
90   *
91   * <p>The rules declaration is the same but an added form is required to capture extra
92   * configuration information.  The form is associated with the validation rule using a
93   * naming convention.  The prefix of the form name is "org.apache.shale.validator.XXXX"
94   * where "XXXX" is the validation rule name.</p>
95   *
96   * <pre>
97   *   &lt;formset&gt;
98   *     &lt;form name="org.apache.shale.validator.mask"&gt;
99   * </pre>
100  *
101  *
102  * <p>The form is followed by a field and the property attribute of the form has the
103  * same value as the rule name.</p>
104  *
105  * <pre>
106  *           &lt;field property="mask"&gt;
107  * </pre>
108  *
109  * <p>Within the field definition, arg's are used to define the parameters in order
110  * for message substitution and method argument value resolution.  There are two reserved
111  * name values for the arg node used to define messages and parameters.</p>
112  *
113  * <pre>
114  *               &lt;arg position="0" name="message" key="arg" resource="false"/&gt;
115  *               &lt;arg position="1" name="message" key="mask" resource="false"/&gt;
116  *               &lt;arg position="2" name="message" key="submittedValue" resource="false"/&gt;
117  *
118  *               &lt;arg position="0" name="parameter" key="submittedValue" resource="false"/&gt;
119  *               &lt;arg position="1" name="parameter" key="mask" resource="false"/&gt;
120  * </pre>
121  *
122  * <p>The "message" name arguments defines the possible <code>MessageFormat</code> parameter substitution
123  * where the "position" corresponds to the substitution parameter.</p>
124  *
125  * <pre>
126  *    errors.invalid={0} is invalid.
127  * </pre>
128  *
129  * <p>The "parameter" arguments define the variable names that hold values for the target validatior method
130  * identified by the validator rule name.  The comma delimited class types in the "methodParms" value list
131  * correspond to the parameters by position.</p>
132  *
133  * <pre>
134  *   methodParams="java.lang.String,java.lang.String"
135  * </pre>
136  *
137  * <p>The var node is also used to explicitly define a JavaScript variable type.  If not
138  * defined, the default is "string".  The var-value is ignored because its captured by
139  * the shale commons validator instance.</p>
140  *
141  * <pre>
142  *               &lt;var&gt;
143  *                  &lt;var-name&gt;mask&lt;/var-name&gt;
144  *                  &lt;var-value&gt;&lt;/var-value&gt;
145  *                  &lt;var-jstype&gt;regexp&lt;/var-jstype&gt;
146  *               &lt;/var&gt;
147  * </pre>
148  *
149  * $Id: CommonsValidator.java 518858 2007-03-16 04:24:27Z gvanmatre $
150  */
151 
152 public class CommonsValidator implements Validator, Serializable {
153 
154 
155     // -------------------------------------------------------- Static Variables
156 
157 
158     /***
159      * Serial version UID.
160      */
161     private static final long serialVersionUID = -1783130706547542798L;
162 
163     /***
164      * <p>Map of standard types: boolean, byte, char, etc.
165      *    and their corresponding classes.</p>
166      */
167     private static Map standardTypes;
168 
169 
170     /***
171      * <p>Localized messages for this class.</p>
172      */
173     private static final Messages messages =
174         new Messages("org.apache.shale.validator.resources.Bundle");
175 
176 
177     /***
178      * <p>Log instance for this class.</p>
179      */
180     private static final Log log = LogFactory.getLog("org.apache.shale.validator");
181 
182 
183     /***
184      * <p>The name of the parent form used for javascript.</p>
185      */
186     private String formName = null;
187 
188     /***
189      * <p>Returns the parent's form name.</p>
190      *
191      * @return form name the validator is contained in
192      */
193     public String getFormName() {
194        return formName;
195     }
196 
197     /***
198      * <p>Sets the validator's owning form name.</p>
199      *
200      * @param formName The new form name
201      */
202     public void setFormName(String formName) {
203        this.formName = formName;
204     }
205 
206 
207     // -------------------------------------------------------- Instance Variables
208 
209     /***
210      * <p>Validator type.</p>
211      */
212     private String type;
213 
214     /***
215      * <p>Enable client-side validation?</p>
216      */
217     private Boolean client;
218 
219 
220     /***
221      * <p>Enable server-side validation?</p>
222      */
223     private Boolean server;
224 
225 
226     /***
227      * <p>The <code>validate</code> method uses this
228      *    as the text of an error message it stores on the
229      *    FacesContext when validation fails.</p>
230      */
231     private String message;
232 
233 
234     /***
235      * <p>Parameters for the specific Commons Validator to be used.</p>
236      */
237     private Map vars = new HashMap();
238 
239     /***
240      * <p>Returns a <code>Map</code> of variables that can be passed to a
241      * commons validator method or used to create a parameterized error
242      * message.  Several of the public properties are contained within
243      * the <code>vars</code> collection.  These include: arg, min, max,
244      * minlength, maxlength, mask, datePatternStrict.</p>
245      *
246      * @return A value paired collection of variables used to invoke a
247      * validator method.
248      */
249     public Map getVars() {
250        return vars;
251     }
252 
253 
254     // -------------------------------------------------------- Transient Variables
255 
256 
257 
258     // ----------------------------------------------------- Property Accessors
259 
260 
261     /***
262      * <p>The setter method for the <code>type</code> property. This property is
263      *    passed through to the Commons Validator.</p>
264      *
265      * @param newValue The new value for the <code>type</code> property.
266      */
267    public void setType(String newValue) {
268        type = newValue;
269    }
270 
271 
272     /***
273      * <p>The getter method for the <code>type</code> property. This property is
274      *    passed through to the Commons Validator.</p>
275      *
276      *  @return validation rule to apply
277      */
278    public String getType() {
279        return type;
280    }
281 
282 
283     /***
284      * <p>The setter method for the <code>client</code> property. This property is
285      *    passed through to the Commons Validator.</p>
286      *
287      * @param newValue The new value for the <code>client</code> property.
288      */
289    public void setClient(Boolean newValue) {
290        client = newValue;
291    }
292 
293 
294     /***
295      * <p>The getter method for the <code>client</code> property. This property is
296      *    passed through to the Commons Validator.</p>
297      *
298      * @return <code>true</code> if using JavaScript validation
299      */
300    public Boolean getClient() {
301        return client;
302    }
303 
304 
305     /***
306      * <p>The setter method for the <code>server</code> property. This property is
307      *    passed through to the Commons Validator.</p>
308      *
309      * @param newValue The new value for the <code>server</code> property.
310      */
311    public void setServer(Boolean newValue) {
312        server = newValue;
313    }
314 
315 
316     /***
317      * <p>The getter method for the <code>server</code> property. This property is
318      *    passed through to the Commons Validator.</p>
319      *
320      * @return <code>true</code> if using server side validation
321      */
322    public Boolean getServer() {
323        return server;
324    }
325 
326 
327     /***
328      * <p>The setter method for the <code>message</code> property. This property is
329      *    passed through to the Commons Validator.</p>
330      *
331      * @param newValue The new value for the <code>message</code> property.
332      */
333    public void setMessage(String newValue) {
334        message = newValue;
335    }
336 
337 
338     /***
339      * <p>The getter method for the <code>message</code> property. This property is
340      *    passed through to the Commons Validator.</p>
341      *
342      * @return validation message override
343      */
344    public String getMessage() {
345        return message;
346    }
347 
348    /***
349     * <p>Name of the <code>arg</code> property in the <code>vars</code> Map.</p>
350     */
351    private static final String ARG_VARNAME = "arg";
352 
353     /***
354      * <p>The setter method for the <code>arg</code> property. This property is
355      *    passed through to the Commons Validator.</p>
356      *
357      * @param newValue The new value for the <code>arg</code> property.
358      */
359    public void setArg(String newValue) {
360        vars.put(ARG_VARNAME, newValue);
361    }
362 
363 
364    /***
365     * <p>Name of the <code>min</code> property in the <code>vars</code> Map.</p>
366     */
367    public static final String MIN_VARNAME = "min";
368 
369     /***
370      * <p>The setter method for the <code>min</code> property. This property is
371      *    passed through to the Commons Validator.</p>
372      *
373      * @param newValue The new value for the <code>min</code> property.
374      */
375    public void setMin(String newValue) {
376        vars.put(MIN_VARNAME, newValue);
377    }
378 
379    /***
380     * <p>Name of the <code>max</code> property in the <code>vars</code> Map.</p>
381     */
382    public static final String MAX_VARNAME = "max";
383 
384     /***
385      * <p>The setter method for the <code>max</code> property. This property is
386      *    passed through to the Commons Validator.</p>
387      *
388      * @param newValue The new value for the <code>max</code> property.
389      */
390    public void setMax(String newValue) {
391        vars.put(MAX_VARNAME, newValue);
392    }
393 
394 
395    /***
396     * <p>Name of the <code>minLength</code> property in the <code>vars</code> Map.</p>
397     */
398    public static final String MIN_LENGTH_VARNAME = "minlength";
399 
400    /***
401      * <p>The setter method for the <code>minlength</code> property. This property is
402      *    passed through to the Commons Validator.</p>
403      *
404      * @param newValue The new value for the <code>minlength</code> property.
405      */
406    public void setMinLength(String newValue) { vars.put(MIN_LENGTH_VARNAME,  newValue); }
407 
408 
409    /***
410     * <p>Name of the <code>maxLength</code> property in the <code>vars</code> Map.</p>
411     */
412    public static final String MAX_LENGTH_VARNAME = "maxlength";
413 
414     /***
415      * <p>The setter method for the <code>maxlength</code> property. This property is
416      *    passed through to the Commons Validator.</p>
417      *
418      * @param newValue The new value for the <code>maxlength</code> property.
419      */
420    public void setMaxLength(String newValue) {
421        vars.put(MAX_LENGTH_VARNAME, newValue);
422    }
423 
424    /***
425     * <p>Name of the <code>mask</code> property in the <code>vars</code> Map.</p>
426     */
427    public static final String MASK_VARNAME = "mask";
428 
429     /***
430      * <p>The setter method for the <code>mask</code> property. This property is
431      *    passed through to the Commons Validator.</p>
432      *
433      * @param newValue The new value for the <code>mask</code> property.
434      */
435    public void setMask(String newValue) {
436        vars.put(MASK_VARNAME, newValue);
437    }
438 
439 
440    /***
441     * <p>Name of the <code>datePatternStrict</code> in the <code>vars</code> Map.</p>
442     */
443    public static final String DATE_PATTERN_STRICT_VARNAME = "datePatternStrict";
444 
445 
446     /***
447      * <p>The setter method for the <code>datePatternStrict</code> property. This property is
448      *    passed through to the Commons Validator.</p>
449      *
450      * @param newValue The new value for the <code>datePatternStrict</code> property.
451      */
452    public void setDatePatternStrict(String newValue) {
453       vars.put(DATE_PATTERN_STRICT_VARNAME, newValue);
454    }
455 
456 
457     /***
458      * <p>Return the validator resources that were configured and cached
459      * at application startup time.</p>
460      */
461     private static ValidatorResources getValidatorResources() {
462 
463        FacesContext context = FacesContext.getCurrentInstance();
464        ExternalContext external = context.getExternalContext();
465        Map applicationMap = external.getApplicationMap();
466        return (ValidatorResources) applicationMap.get(Globals.VALIDATOR_RESOURCES);
467 
468     }
469 
470 
471     /***
472      * <p>Name of the <code>message</code> property in the <code>vars</code> Map.</p>
473      */
474     private static final String MESSAGE_ARG_NAME = "message";
475 
476     /***
477      * <p>Returns an array of values for message parameter replacement
478      * arguments.  The list and ordering is determined by a form
479      * registered in the common validators XML.  The form name
480      * and the fields property is tied to the validation rule name
481      * by convention.  The the <code>arg</code> name attribute is
482      * assumed to be "message" for message argument grouping.</p>
483      *
484      * @param ruleName name of the validation rule
485      * @param localVars snapshot of EL vars captured at rendering time
486      *        and used by the script collector
487      * @return array of objects used to fill the message
488      */
489     protected Object[] getMessageArgs(String ruleName, Map localVars) {
490 
491        Tags tagUtils = new Tags();
492        Arg[] templateArgs = getArgs(MESSAGE_ARG_NAME, ruleName);
493        assert templateArgs != null;
494 
495        Object[] target = new Object[templateArgs.length];
496 
497        for (int i = 0; i < templateArgs.length; i++) {
498           Object value = vars.get(templateArgs[i].getKey());
499 
500           // look for a local var override
501           if (localVars != null && localVars.containsKey(templateArgs[i].getKey())) {
502              value = localVars.get(templateArgs[i].getKey());
503           } else if (value != null && value instanceof String) {
504               // if a String, check for a value binding expression
505               value = tagUtils.eval((String) value);
506           }
507           target[i] = value;
508        }
509 
510        return target;
511     }
512 
513     /***
514      * <p>Returns an array of class types corresponding to the the
515      * target validation rules method signature.  The params are
516      * configured by the <code>validator</code>'s <code>methodParams</code>
517      * attribute.</p>
518      *
519      * @param validationAction the validators configuration bean populated from the XML file.
520      * @return an array of class types for the formal parameter list.
521      * @throws ClassNotFoundException validation rule class not found
522      */
523     protected Class[] loadMethodParamClasses(ValidatorAction validationAction) throws ClassNotFoundException {
524 
525         List tmp = new ArrayList();
526         StringTokenizer tokenizer = new StringTokenizer(validationAction.getMethodParams(), ",");
527         while (tokenizer.hasMoreTokens()) {
528             String token = tokenizer.nextToken().trim();
529             if (token.length() > 0) {
530                 tmp.add(token);
531             }
532         }
533 
534         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
535         if (classLoader == null) {
536             classLoader = this.getClass().getClassLoader();
537         }
538         assert classLoader != null;
539 
540 
541         Class[] parameterClasses = new Class[tmp.size()];
542         for (int i = 0; i < tmp.size(); i++) {
543             String className = (String) tmp.get(i);
544             if (standardTypes.containsKey(className)) {
545                 parameterClasses[i] = (Class) standardTypes.get(className);
546             } else {
547                 parameterClasses[i] = classLoader.loadClass(className);
548             }
549         }
550 
551         return parameterClasses;
552     }
553 
554 
555     /***
556      * <p>The args name used to define the parameter values for
557      * a validation rule.</p>
558      */
559     private static final String PARAMETER_ARG_NAME = "parameter";
560 
561     /***
562      * <p>Returns an array of parameter names in the target validator's
563      * method.  The parameter names are defined in the validators configuration
564      * file under a form and field that correspond to the target rule.
565      * The name attribute of the nested <code>arg</code> is assumed to be
566      * "parameter".
567      *
568      * @param name the name of the target validation rule
569      * @return array of formal parameter names
570      */
571     public String[] getMethodParamNames(String name) {
572         Arg[] templateArgs = getArgs(PARAMETER_ARG_NAME, name);
573         assert templateArgs != null;
574 
575         String[] target = new String[templateArgs.length];
576 
577         for (int i = 0; i < templateArgs.length; i++) {
578            target[i] = templateArgs[i].getKey();
579         }
580 
581         return target;
582     }
583 
584     private static final String FORM_NAME_PREFIX = "org.apache.shale.validator.";
585 
586     /***
587      * <p>Returns validator <code>Arg</code> beans from the configuration file.
588      * The <code>form</code> and <code>field</code> nodes that contain the target
589      * arguments have a naming convention to a validation rule.  This convention
590      * was adopted for JSF validators because the <code>var</code>'s of the defining
591      * validator is defined by the JSF validator object and not in the config file.
592      * The JSF implementation doesn't have a concept of defining global form field
593      * characteristics outside of the associated JSF component.  But, we needed
594      * a place to explicitly declare parameter names and message arguments.<p>
595      *
596      * @param name the name of the <code>arg</code> name attribute.
597      * @param ruleName the name of the validator rule
598      * @return an array of validator <code>Arg</code> beans.
599      */
600     protected static Arg[] getArgs(String name, String ruleName) {
601 
602         StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX);
603         formName.append(ruleName);
604 
605         Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString());
606         assert formDef != null;
607 
608         Field field = formDef.getField(ruleName);
609         assert field != null;
610 
611         Arg[] templateArgs = field.getArgs(name);
612 
613         int max = -1;
614         for (int i = templateArgs.length - 1; i > -1; i--) {
615            if (templateArgs[i] != null) {
616               max = i;
617               break;
618            }
619         }
620         if (max == -1) {
621            return new Arg[0];
622         } else if (max < templateArgs.length - 1) {
623            Arg[] tmp = new Arg[max + 1];
624            System.arraycopy(templateArgs, 0, tmp, 0, max + 1);
625            templateArgs = tmp;
626         }
627         return templateArgs;
628     }
629 
630 
631     /***
632      * <p>Returns the JavaScript type for a <code>var</code> collection
633      * item.  The default is <code>Var.JSTYPE_STRING</code>.  The type
634      * can be overridden by adding a <code>var</code> node to the
635      * <code>field</code> node in the validator configuration file.
636      * The <code>var-value</code> is ignored but a value is required
637      * for well-formness.  The <code>var-jstype</code>
638      * and <code>var-name</code> are the only values used.
639      * The <code>form</code> and the <code>field</code> the <code>var</code>
640      * is nested under has an association with the <code>type</code>.
641      * </p>
642      * @param varName The name of the target variable
643      * @return The JavaScript variable type ("string", "int", "regexp")
644      */
645     public String getVarType(String varName) {
646         StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX);
647         formName.append(getType());
648 
649         Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString());
650         assert formDef != null;
651 
652         Field field = formDef.getField(getType());
653         assert field != null;
654 
655         String jsType = Var.JSTYPE_STRING; // default type
656         Var var = field.getVar(varName);
657 
658         if (var != null && var.getJsType() != null) {
659            jsType = var.getJsType();
660         }
661 
662         return jsType;
663     }
664 
665 
666     /***
667      * <p>Assumed argument name that captures the javascript
668      * validation rule callback.</p>
669      */
670     private static final String JSCALLBACK_ARG_NAME = "jscallback";
671 
672 
673     /***
674      * <p>Returns a mnemonic used to build the commons validator
675      * javascript call back.  This method is invoked from the
676      * {@link org.apache.shale.component.ValidatorScript} component.
677      * The routine looks for an <code>arg</code> with a name of
678      * <code>jscallback</code> under a form name and field
679      * property corresponding to the rule.  If there is not
680      * a matching arg found, the <code>ruleName</code> is
681      * returned as the default.
682      * </p>
683      * @param ruleName name of the target rule to invoke
684      * @return code used to create the javacript call back function
685      */
686     public static String getJsCallbackMnemonic(String ruleName) {
687         Arg[] args = getArgs(JSCALLBACK_ARG_NAME, ruleName);
688         if (args == null || args.length == 0) {
689            return ruleName;
690         } else {
691            return args[0].getKey();
692         }
693     }
694 
695     /***
696      * <p>Loads an array of method parameter values corresponding to the
697      * formal parameter of the target validator's method.<p>
698      * @param context faces context
699      * @param validatorAction <code>ValidatorAction</code> configuration bean.
700      * @param methodParamClasses <code>Class[]</code> of the parameters of the target method.
701      * @param localVars snapshot of EL variables at rendering time; used by client side
702      * @return An array of object valuse for each method parameter.
703      */
704     protected Object[] loadMethodParamValues(FacesContext context, ValidatorAction validatorAction, Class[] methodParamClasses, Map localVars) {
705         Tags tagUtils = new Tags();
706 
707         String[] paramNames = getMethodParamNames(validatorAction.getName());
708         assert paramNames != null;
709 
710         Object[] target = new Object[paramNames.length];
711         assert paramNames.length == methodParamClasses.length;
712 
713         for (int i = 0; i < paramNames.length; i++) {
714             Object obj = null;
715             if (localVars != null && localVars.containsKey(paramNames[i])) {
716                 obj = localVars.get(paramNames[i]);   
717             }
718             if (obj == null) {
719                 obj = vars.get(paramNames[i]);
720             }
721             if (obj != null && obj instanceof String) {
722                 obj = tagUtils.eval((String) obj);
723             }
724             target[i] = convert(context, obj, methodParamClasses[i]);
725         }
726 
727         return target;
728      }
729 
730 
731     /***
732      * <p>Returns the Commons validator action that's appropriate
733      *    for the validator with the given <code>name</code>.</p>
734      *
735      * @param name The name of the validator
736      * @return Validator rules config bean
737      */
738    public static ValidatorAction getValidatorAction(String name) {
739       return getValidatorResources().getValidatorAction(name);
740    }
741    /***
742     * <p>Returns the commons validator action associated with
743     * the <code>type</code> attribute.</p>
744     *
745     * @return Validator rules config bean
746     */
747    public ValidatorAction getValidatorAction() {
748        return getValidatorResources().getValidatorAction(getType());
749    }
750 
751    /***
752     * <p>For a given commons validator rule, returns an array of
753     * rule names that are dependent of the <code>name</code>.
754     * Rule dependencies will be first in the returned array.
755     * </p>
756     * @param name target validator rule
757     * @return array of all dependent rules for the target name
758     */
759    protected String[] getDependencies(String name) {
760 
761        ValidatorAction action = getValidatorAction(name);
762        assert action != null;
763 
764        List dependencies = action.getDependencyList();
765        String[] types = new String[dependencies.size() + 1];
766 
767        for (int i = 0; i < dependencies.size(); i++) {
768            types[i] = (String) dependencies.get(i);
769        }
770        types[types.length - 1] = name;
771 
772        return types;
773    }
774 
775    /***
776     * <p>Name used to store the component's submitted value in the
777     * <code>vars</code> Map.</p>
778     */
779    private static final String SUBMITTED_VALUE_VARNAME = "submittedValue";
780 
781     /***
782      * <p>This <code>validate</code> method is called by JSF to verify
783      *    the component to which the validator is attached.</p>
784      *
785      * @param context The faces context
786      * @param component The component to validate
787      * @param value the component's submitted value after the converter applied.
788      */
789    public void validate(FacesContext context, UIComponent component, Object value) {
790 
791        if (Boolean.FALSE.equals(getServer())) {
792            return;
793        }
794        String[] types = getDependencies(getType());
795        for (int j = 0; j < types.length; j++) {
796            ValidatorAction validatorAction = CommonsValidator.getValidatorAction(types[j]);
797 
798            try {
799                if (component instanceof EditableValueHolder) {
800                   vars.put(SUBMITTED_VALUE_VARNAME, ((EditableValueHolder) component).getSubmittedValue());    
801                } else {
802                   vars.put(SUBMITTED_VALUE_VARNAME, value);
803                }
804 
805                Map localVars = null;
806                // A map that captures information about a component that might contain state for commons
807                // validators properties.  The map is organized by a hierarchy "clientId/validatorType/vars".
808                // It is captured at render time.
809                Map ids = (Map) component.getAttributes().get(ValidatorInputRenderer.VALIDATOR_CLIENTIDS_ATTR);
810                if (ids != null) {
811                    String clientId = component.getClientId(context);
812                    Map validatorVars = (Map) ids.get(clientId);
813                    if (validatorVars != null) {
814                       localVars = (Map) validatorVars.get(getType()); 
815                    }
816                }
817 
818                Class validatorClass = loadValidatorClass(validatorAction);
819                Class[] paramClasses = this.loadMethodParamClasses(validatorAction);
820                Object[] paramValues = this.loadMethodParamValues(context, validatorAction, paramClasses, localVars);
821                Method validatorMethod = this.loadValidatorMethod(validatorAction, validatorClass,  paramClasses);
822                Object validator = null;
823                if (!Modifier.isStatic(validatorMethod.getModifiers())) {
824                    validator = validatorClass.newInstance();
825                }
826                Boolean r = (Boolean) validatorMethod.invoke(validator, paramValues);
827                
828                if (r.equals(Boolean.FALSE)) {
829                    throw new ValidatorException(new FacesMessage(
830                            FacesMessage.SEVERITY_ERROR,
831                            getErrorMessage(context, validatorAction, localVars), null));
832                }
833            } catch (IllegalArgumentException e) {
834                throw new RuntimeException(messages.getMessage("commonsValidator.intException",
835                        new Object[] {getType(), component.getId()}), e);
836            } catch (ClassNotFoundException e) {
837                throw new RuntimeException(messages.getMessage("commonsValidator.intException",
838                        new Object[] {getType(), component.getId()}), e);
839            } catch (InstantiationException e) {
840                throw new RuntimeException(messages.getMessage("commonsValidator.intException",
841                        new Object[] {getType(), component.getId()}), e);
842            } catch (IllegalAccessException e) {
843                throw new RuntimeException(messages.getMessage("commonsValidator.intException",
844                        new Object[] {getType(), component.getId()}), e);
845            } catch (NoSuchMethodException e) {
846                throw new RuntimeException(messages.getMessage("commonsValidator.intException",
847                        new Object[] {getType(), component.getId()}), e);
848            } catch (InvocationTargetException e) {
849                throw new RuntimeException(messages.getMessage("commonsValidator.intException",
850                        new Object[] {getType(), component.getId()}), e);
851            } finally {
852                vars.remove(SUBMITTED_VALUE_VARNAME);
853            }
854 
855        }
856    }
857 
858 
859    /***
860     * <p>Loads the commons validator class containing the target rule.</p>
861     * @param validatorAction the validator rules config bean
862     * @return the class having the target validation method
863     *
864     * @throws ClassNotFoundException if the specified class cannot be found
865     * @throws InstantiationException if a new instance cannot be instantiated
866     * @throws IllegalAccessException if there is no public constructor
867     */
868    protected Class loadValidatorClass(ValidatorAction validatorAction)
869      throws ClassNotFoundException, InstantiationException, IllegalAccessException {
870 
871        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
872        if (classLoader == null) {
873            classLoader = this.getClass().getClassLoader();
874        }
875        assert classLoader != null;
876 
877        //find the target class having the validator rule
878        Class validatorClass = classLoader.loadClass(validatorAction.getClassname());
879 
880        return validatorClass;
881    }
882 
883    /***
884     * <p>Loads the <code>Method</code> of the <code>validatorClass</code> having
885     * using definitions from the <code>validatorAction</code> bean.
886     * </p>
887     * @param validatorAction The config info bean of the target rule.
888     * @param validatorClass The class having the validation method.
889     * @param methodParamClasses The method formal parameter class signature.
890     * @return target commons validator method to invoke.
891     * @throws NoSuchMethodException if the specified method cannot be found
892     */
893    protected Method loadValidatorMethod(ValidatorAction validatorAction,
894            Class validatorClass,
895            Class[] methodParamClasses) throws NoSuchMethodException {
896        //find the method on the target validator rule class
897        Method validatorMethod = validatorClass.getMethod(validatorAction.getMethod(), methodParamClasses);
898        return validatorMethod;
899    }
900 
901 
902     /***
903      * <p>Retrieves an error message, using the validator's message combined
904      *    with the errant value.</p>
905      * @param context the all knowing faces conext object.
906      * @param validatorAction config bean defining the commons validator rule.
907      * @param localVars snapshot of EL variables at rendering time; used by client side
908      * @return the message after parameter substitution.
909      */
910    public String getErrorMessage(FacesContext context, ValidatorAction validatorAction, Map localVars) {
911       final String DEFAULT_BUNDLE_NAME = "org.apache.shale.validator.messages";
912 
913       Locale locale = context.getViewRoot().getLocale();
914       String msg = getMessage();
915       if (msg == null) {
916          String msgkey = validatorAction.getMsg();
917          ClassLoader loader = Thread.currentThread().getContextClassLoader();
918          if (loader == null) { loader = getClass().getClassLoader(); }
919          Application app = context.getApplication();
920          String appBundleName = app.getMessageBundle();
921          if (appBundleName != null) {
922             ResourceBundle bundle
923                = ResourceBundle.getBundle(appBundleName, locale, loader);
924             if (bundle != null) {
925                try {
926                   msg = bundle.getString(msgkey);
927                } catch (MissingResourceException ex) {
928                    ; // Ignore this
929                }
930             }
931          }
932          if (msg == null) {
933             ResourceBundle bundle
934                = ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, loader);
935             if (bundle != null) {
936                try {
937                   msg = bundle.getString(msgkey);
938                } catch (MissingResourceException ex) {
939                    log.error(messages.getMessage("commonsValidator.msgerror",
940                        new Object[] {msgkey}), ex);
941                    throw ex;
942                }
943             }
944          }
945       }
946       Object[] params = getMessageArgs(validatorAction.getName(), localVars);
947       msg = new MessageFormat(msg, locale).format(params);
948       return msg;
949    }
950 
951 
952     // ----------------------------------------------------- Static Initialization
953 
954     /***
955      * <p>A utility method that converts an object to an instance
956      *    of a given class, such as converting <code>"true"</code>
957      *    for example, into <code>Boolean.TRUE</code>.</p>
958      * <p>If the component passed to this method is an instance of
959      *    <code>EditableValueHolder</code> and the object's class is
960      *    <code>String</code>, this method returns the component's
961      *     submitted value, without converting it to a string. The
962      *     <code>component</code> parameter can be <code>null</code>.
963      *  </p>
964      *
965      * @param context faces context
966      * @param obj The object to convert
967      * @param cl The type of object to convert to
968      * @return target object type
969      */
970    private static Object convert(FacesContext context, Object obj, Class cl) {
971 
972       ConverterHelper converterHelper = new ConverterHelper();
973       // correct target type
974       if (cl.isInstance(obj)) {
975           return obj;
976       }
977       // target type is String
978       if (cl == String.class) {
979           if (obj instanceof String) {
980              return obj;
981           } else {
982              return converterHelper.asString(context, obj.getClass(), obj);
983           }
984       }
985       
986       if (obj instanceof String) {   
987           // String to object
988           return converterHelper.asObject(context, cl, (String) obj);  
989       } else {
990           //Object to String
991           String source = converterHelper.asString(context, obj.getClass(), obj);
992           // String to Object
993           return converterHelper.asObject(context, cl, source);
994       }
995 
996    }
997 
998 
999     /***
1000      * <p>A utility method that returns <code>true</code> if
1001      *    the supplied string has a length greater than zero.
1002      * </p>
1003      *
1004      * @param str The string
1005      * @return <code>true</code> if not an empty String
1006      */
1007    // these two methods are referenced in validator-utils.xml
1008    public static boolean isSupplied(String str) {
1009       return str.trim().length() > 0;
1010    }
1011 
1012 
1013     /***
1014      * <p>A utility method that returns <code>true</code> if
1015      *    the supplied string represents a date.</p>
1016      *
1017      * @param d The string representation of the date.
1018      * @param datePatternStrict Commons validator property
1019      * @return <code>true</code> if <code>d</code> is a date
1020      */
1021    public static boolean isDate(String d, String datePatternStrict) {
1022       return GenericValidator.isDate(d, datePatternStrict, true);
1023    }
1024 
1025 
1026     // ----------------------------------------------------- Static Initialization
1027 
1028 
1029     /***
1030      * <p>Standard types for conversions</p>
1031      */
1032    static {
1033       standardTypes = new HashMap();
1034       standardTypes.put("boolean", boolean.class);
1035       standardTypes.put("byte", byte.class);
1036       standardTypes.put("char", char.class);
1037       standardTypes.put("double", double.class);
1038       standardTypes.put("float", float.class);
1039       standardTypes.put("int", int.class);
1040       standardTypes.put("long", long.class);
1041       standardTypes.put("short", short.class);
1042       standardTypes.put("java.lang.String", String.class);
1043    }
1044 
1045 }