2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 * <global>
80 * <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=""/>
88 * </global>
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 * <formset>
98 * <form name="org.apache.shale.validator.mask">
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 * <field property="mask">
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 * <arg position="0" name="message" key="arg" resource="false"/>
115 * <arg position="1" name="message" key="mask" resource="false"/>
116 * <arg position="2" name="message" key="submittedValue" resource="false"/>
117 *
118 * <arg position="0" name="parameter" key="submittedValue" resource="false"/>
119 * <arg position="1" name="parameter" key="mask" resource="false"/>
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 * <var>
143 * <var-name>mask</var-name>
144 * <var-value></var-value>
145 * <var-jstype>regexp</var-jstype>
146 * </var>
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
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
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
255
256
257
258
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
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
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;
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
807
808
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
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
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 ;
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
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
974 if (cl.isInstance(obj)) {
975 return obj;
976 }
977
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
988 return converterHelper.asObject(context, cl, (String) obj);
989 } else {
990
991 String source = converterHelper.asString(context, obj.getClass(), obj);
992
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
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
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 }