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