KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > validator > ValidatorAction


1 /*
2  * $Id: ValidatorAction.java 164749 2005-04-26 06:15:17Z mrdon $
3  * $Rev$
4  * $Date: 2005-04-25 23:15:17 -0700 (Mon, 25 Apr 2005) $
5  *
6  * ====================================================================
7  * Copyright 2001-2005 The Apache Software Foundation
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package org.apache.commons.validator;
23
24 import java.io.BufferedReader JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.Serializable JavaDoc;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30 import java.lang.reflect.Method JavaDoc;
31 import java.lang.reflect.Modifier JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.StringTokenizer JavaDoc;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.commons.validator.util.ValidatorUtils;
41
42 /**
43  * Contains the information to dynamically create and run a validation
44  * method. This is the class representation of a pluggable validator that can
45  * be defined in an xml file with the <validator> element.
46  *
47  * <strong>Note</strong>: The validation method is assumed to be thread safe.
48  */

49 public class ValidatorAction implements Serializable JavaDoc {
50     
51     /**
52      * Logger.
53      */

54     private static final Log log = LogFactory.getLog(ValidatorAction.class);
55
56     /**
57      * The name of the validation.
58      */

59     private String JavaDoc name = null;
60
61     /**
62      * The full class name of the class containing
63      * the validation method associated with this action.
64      */

65     private String JavaDoc classname = null;
66     
67     /**
68      * The Class object loaded from the classname.
69      */

70     private Class JavaDoc validationClass = null;
71
72     /**
73      * The full method name of the validation to be performed. The method
74      * must be thread safe.
75      */

76     private String JavaDoc method = null;
77     
78     /**
79      * The Method object loaded from the method name.
80      */

81     private Method JavaDoc validationMethod = null;
82
83     /**
84      * <p>
85      * The method signature of the validation method. This should be a comma
86      * delimited list of the full class names of each parameter in the correct
87      * order that the method takes.
88      * </p>
89      * <p>
90      * Note: <code>java.lang.Object</code> is reserved for the
91      * JavaBean that is being validated. The <code>ValidatorAction</code>
92      * and <code>Field</code> that are associated with a field's
93      * validation will automatically be populated if they are
94      * specified in the method signature.
95      * </p>
96      */

97     private String JavaDoc methodParams =
98             Validator.BEAN_PARAM
99             + ","
100             + Validator.VALIDATOR_ACTION_PARAM
101             + ","
102             + Validator.FIELD_PARAM;
103             
104     /**
105      * The Class objects for each entry in methodParameterList.
106      */

107     private Class JavaDoc[] parameterClasses = null;
108
109     /**
110      * The other <code>ValidatorAction</code>s that this one depends on. If
111      * any errors occur in an action that this one depends on, this action will
112      * not be processsed.
113      */

114     private String JavaDoc depends = null;
115
116     /**
117      * The default error message associated with this action.
118      */

119     private String JavaDoc msg = null;
120
121     /**
122      * An optional field to contain the name to be used if JavaScript is
123      * generated.
124      */

125     private String JavaDoc jsFunctionName = null;
126
127     /**
128      * An optional field to contain the class path to be used to retrieve the
129      * JavaScript function.
130      */

131     private String JavaDoc jsFunction = null;
132
133     /**
134      * An optional field to containing a JavaScript representation of the
135      * java method assocated with this action.
136      */

137     private String JavaDoc javascript = null;
138
139     /**
140      * If the java method matching the correct signature isn't static, the
141      * instance is stored in the action. This assumes the method is thread
142      * safe.
143      */

144     private Object JavaDoc instance = null;
145
146     /**
147      * An internal List representation of the other <code>ValidatorAction</code>s
148      * this one depends on (if any). This List gets updated
149      * whenever setDepends() gets called. This is synchronized so a call to
150      * setDepends() (which clears the List) won't interfere with a call to
151      * isDependency().
152      */

153     private List JavaDoc dependencyList = Collections.synchronizedList(new ArrayList JavaDoc());
154
155     /**
156      * An internal List representation of all the validation method's
157      * parameters defined in the methodParams String.
158      */

159     private List JavaDoc methodParameterList = new ArrayList JavaDoc();
160
161     /**
162      * Gets the name of the validator action.
163      */

164     public String JavaDoc getName() {
165         return name;
166     }
167
168     /**
169      * Sets the name of the validator action.
170      */

171     public void setName(String JavaDoc name) {
172         this.name = name;
173     }
174
175     /**
176      * Gets the class of the validator action.
177      */

178     public String JavaDoc getClassname() {
179         return classname;
180     }
181
182     /**
183      * Sets the class of the validator action.
184      */

185     public void setClassname(String JavaDoc classname) {
186         this.classname = classname;
187     }
188
189     /**
190      * Gets the name of method being called for the validator action.
191      */

192     public String JavaDoc getMethod() {
193         return method;
194     }
195
196     /**
197      * Sets the name of method being called for the validator action.
198      */

199     public void setMethod(String JavaDoc method) {
200         this.method = method;
201     }
202
203     /**
204      * Gets the method parameters for the method.
205      */

206     public String JavaDoc getMethodParams() {
207         return methodParams;
208     }
209
210     /**
211      * Sets the method parameters for the method.
212      * @param methodParams A comma separated list of parameters.
213      */

214     public void setMethodParams(String JavaDoc methodParams) {
215         this.methodParams = methodParams;
216
217         this.methodParameterList.clear();
218
219         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(methodParams, ",");
220         while (st.hasMoreTokens()) {
221             String JavaDoc value = st.nextToken().trim();
222
223             if (value != null && value.length() > 0) {
224                 this.methodParameterList.add(value);
225             }
226         }
227     }
228
229     /**
230      * Gets the dependencies of the validator action as a comma separated list
231      * of validator names.
232      */

233     public String JavaDoc getDepends() {
234         return this.depends;
235     }
236
237     /**
238      * Sets the dependencies of the validator action.
239      * @param depends A comma separated list of validator names.
240      */

241     public void setDepends(String JavaDoc depends) {
242         this.depends = depends;
243
244         this.dependencyList.clear();
245
246         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(depends, ",");
247         while (st.hasMoreTokens()) {
248             String JavaDoc depend = st.nextToken().trim();
249
250             if (depend != null && depend.length() > 0) {
251                 this.dependencyList.add(depend);
252             }
253         }
254     }
255
256     /**
257      * Gets the message associated with the validator action.
258      */

259     public String JavaDoc getMsg() {
260         return msg;
261     }
262
263     /**
264      * Sets the message associated with the validator action.
265      */

266     public void setMsg(String JavaDoc msg) {
267         this.msg = msg;
268     }
269
270     /**
271      * Gets the Javascript function name. This is optional and can
272      * be used instead of validator action name for the name of the
273      * Javascript function/object.
274      */

275     public String JavaDoc getJsFunctionName() {
276         return jsFunctionName;
277     }
278
279     /**
280      * Sets the Javascript function name. This is optional and can
281      * be used instead of validator action name for the name of the
282      * Javascript function/object.
283      */

284     public void setJsFunctionName(String JavaDoc jsFunctionName) {
285         this.jsFunctionName = jsFunctionName;
286     }
287
288     /**
289      * Sets the fully qualified class path of the Javascript function.
290      * <p>
291      * This is optional and can be used <strong>instead</strong> of the setJavascript().
292      * Attempting to call both <code>setJsFunction</code> and <code>setJavascript</code>
293      * will result in an <code>IllegalStateException</code> being thrown. </p>
294      * <p>
295      * If <strong>neither</strong> setJsFunction or setJavascript is set then
296      * validator will attempt to load the default javascript definition.
297      * </p>
298      * <pre>
299      * <b>Examples</b>
300      * If in the validator.xml :
301      * #1:
302      * &lt;validator name="tire"
303      * jsFunction="com.yourcompany.project.tireFuncion"&gt;
304      * Validator will attempt to load com.yourcompany.project.validateTireFunction.js from
305      * its class path.
306      * #2:
307      * &lt;validator name="tire"&gt;
308      * Validator will use the name attribute to try and load
309      * org.apache.commons.validator.javascript.validateTire.js
310      * which is the default javascript definition.
311      * </pre>
312      */

313     public void setJsFunction(String JavaDoc jsFunction) {
314         if (javascript != null) {
315             throw new IllegalStateException JavaDoc("Cannot call setJsFunction() after calling setJavascript()");
316         }
317
318         this.jsFunction = jsFunction;
319     }
320
321     /**
322      * Gets the Javascript equivalent of the java class and method
323      * associated with this action.
324      */

325     public String JavaDoc getJavascript() {
326         return javascript;
327     }
328
329     /**
330      * Sets the Javascript equivalent of the java class and method
331      * associated with this action.
332      */

333     public void setJavascript(String JavaDoc javascript) {
334         if (jsFunction != null) {
335             throw new IllegalStateException JavaDoc("Cannot call setJavascript() after calling setJsFunction()");
336         }
337
338         this.javascript = javascript;
339     }
340
341     /**
342      * Initialize based on set.
343      */

344     protected void init() {
345         this.loadJavascriptFunction();
346     }
347
348     /**
349      * Load the javascript function specified by the given path. For this
350      * implementation, the <code>jsFunction</code> property should contain a
351      * fully qualified package and script name, separated by periods, to be
352      * loaded from the class loader that created this instance.
353      *
354      * TODO if the path begins with a '/' the path will be intepreted as
355      * absolute, and remain unchanged. If this fails then it will attempt to
356      * treat the path as a file path. It is assumed the script ends with a
357      * '.js'.
358      */

359     protected synchronized void loadJavascriptFunction() {
360
361         if (this.javascriptAlreadyLoaded()) {
362             return;
363         }
364
365         if (log.isTraceEnabled()) {
366             log.trace(" Loading function begun");
367         }
368
369         if (this.jsFunction == null) {
370             this.jsFunction = this.generateJsFunction();
371         }
372
373         String JavaDoc javascriptFileName = this.formatJavascriptFileName();
374
375         if (log.isTraceEnabled()) {
376             log.trace(" Loading js function '" + javascriptFileName + "'");
377         }
378
379         this.javascript = this.readJavascriptFile(javascriptFileName);
380
381         if (log.isTraceEnabled()) {
382             log.trace(" Loading javascript function completed");
383         }
384
385     }
386
387     /**
388      * Read a javascript function from a file.
389      * @param javascriptFileName The file containing the javascript.
390      * @return The javascript function or null if it could not be loaded.
391      */

392     private String JavaDoc readJavascriptFile(String JavaDoc javascriptFileName) {
393         ClassLoader JavaDoc classLoader = Thread.currentThread().getContextClassLoader();
394         if (classLoader == null) {
395             classLoader = this.getClass().getClassLoader();
396         }
397
398         InputStream JavaDoc is = classLoader.getResourceAsStream(javascriptFileName);
399         if (is == null) {
400             is = this.getClass().getResourceAsStream(javascriptFileName);
401         }
402
403         if (is == null) {
404             log.debug(" Unable to read javascript name "+javascriptFileName);
405             return null;
406         }
407
408         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
409         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is));
410         try {
411             String JavaDoc line = null;
412             while ((line = reader.readLine()) != null) {
413                 buffer.append(line + "\n");
414             }
415
416         } catch(IOException JavaDoc e) {
417             log.error("Error reading javascript file.", e);
418
419         } finally {
420             try {
421                 reader.close();
422             } catch(IOException JavaDoc e) {
423                 log.error("Error closing stream to javascript file.", e);
424             }
425         }
426         
427         String JavaDoc function = buffer.toString();
428         return function.equals("") ? null : function;
429     }
430
431     /**
432      * @return A filename suitable for passing to a
433      * ClassLoader.getResourceAsStream() method.
434      */

435     private String JavaDoc formatJavascriptFileName() {
436         String JavaDoc name = this.jsFunction.substring(1);
437
438         if (!this.jsFunction.startsWith("/")) {
439             name = jsFunction.replace('.', '/') + ".js";
440         }
441
442         return name;
443     }
444
445     /**
446      * @return true if the javascript for this action has already been loaded.
447      */

448     private boolean javascriptAlreadyLoaded() {
449         return (this.javascript != null);
450     }
451
452     /**
453      * Used to generate the javascript name when it is not specified.
454      */

455     private String JavaDoc generateJsFunction() {
456         StringBuffer JavaDoc jsName =
457                 new StringBuffer JavaDoc("org.apache.commons.validator.javascript");
458
459         jsName.append(".validate");
460         jsName.append(name.substring(0, 1).toUpperCase());
461         jsName.append(name.substring(1, name.length()));
462
463         return jsName.toString();
464     }
465
466     /**
467      * Checks whether or not the value passed in is in the depends field.
468      */

469     public boolean isDependency(String JavaDoc validatorName) {
470         return this.dependencyList.contains(validatorName);
471     }
472
473     /**
474      * Returns the dependent validator names as an unmodifiable
475      * <code>List</code>.
476      */

477     public List JavaDoc getDependencyList() {
478         return Collections.unmodifiableList(this.dependencyList);
479     }
480
481     /**
482      * Returns a string representation of the object.
483      */

484     public String JavaDoc toString() {
485         StringBuffer JavaDoc results = new StringBuffer JavaDoc("ValidatorAction: ");
486         results.append(name);
487         results.append("\n");
488
489         return results.toString();
490     }
491     
492     /**
493      * Dynamically runs the validation method for this validator and returns
494      * true if the data is valid.
495      * @param field
496      * @param params A Map of class names to parameter values.
497      * @param results
498      * @param pos The index of the list property to validate if it's indexed.
499      * @throws ValidatorException
500      */

501     boolean executeValidationMethod(
502         Field field,
503         Map JavaDoc params,
504         ValidatorResults results,
505         int pos)
506         throws ValidatorException {
507
508         params.put(Validator.VALIDATOR_ACTION_PARAM, this);
509
510         try {
511             ClassLoader JavaDoc loader = this.getClassLoader(params);
512             this.loadValidationClass(loader);
513             this.loadParameterClasses(loader);
514             this.loadValidationMethod();
515
516             Object JavaDoc[] paramValues = this.getParameterValues(params);
517             
518             if (field.isIndexed()) {
519                 this.handleIndexedField(field, pos, paramValues);
520             }
521
522             Object JavaDoc result = null;
523             try {
524                 result =
525                     validationMethod.invoke(
526                         getValidationClassInstance(),
527                         paramValues);
528
529             } catch (IllegalArgumentException JavaDoc e) {
530                 throw new ValidatorException(e.getMessage());
531             } catch (IllegalAccessException JavaDoc e) {
532                 throw new ValidatorException(e.getMessage());
533             } catch (InvocationTargetException JavaDoc e) {
534
535                 if (e.getTargetException() instanceof Exception JavaDoc) {
536                     throw (Exception JavaDoc) e.getTargetException();
537
538                 } else if (e.getTargetException() instanceof Error JavaDoc) {
539                     throw (Error JavaDoc) e.getTargetException();
540                 }
541             }
542
543             boolean valid = this.isValid(result);
544             if (!valid || (valid && !onlyReturnErrors(params))) {
545                 results.add(field, this.name, valid, result);
546             }
547
548             if (!valid) {
549                 return false;
550             }
551
552             // TODO This catch block remains for backward compatibility. Remove
553
// this for Validator 2.0 when exception scheme changes.
554
} catch (Exception JavaDoc e) {
555             if (e instanceof ValidatorException) {
556                 throw (ValidatorException) e;
557             }
558
559             log.error(
560                 "Unhandled exception thrown during validation: " + e.getMessage(),
561                 e);
562
563             results.add(field, this.name, false);
564             return false;
565         }
566
567         return true;
568     }
569     
570     /**
571      * Load the Method object for the configured validation method name.
572      * @throws ValidatorException
573      */

574     private void loadValidationMethod() throws ValidatorException {
575         if (this.validationMethod != null) {
576             return;
577         }
578      
579         try {
580             this.validationMethod =
581                 this.validationClass.getMethod(this.method, this.parameterClasses);
582      
583         } catch (NoSuchMethodException JavaDoc e) {
584             throw new ValidatorException("No such validation method: " +
585                 e.getMessage());
586         }
587     }
588     
589     /**
590      * Load the Class object for the configured validation class name.
591      * @param loader The ClassLoader used to load the Class object.
592      * @throws ValidatorException
593      */

594     private void loadValidationClass(ClassLoader JavaDoc loader)
595         throws ValidatorException {
596         
597         if (this.validationClass != null) {
598             return;
599         }
600         
601         try {
602             this.validationClass = loader.loadClass(this.classname);
603         } catch (ClassNotFoundException JavaDoc e) {
604             throw new ValidatorException(e.getMessage());
605         }
606     }
607     
608     /**
609      * Converts a List of parameter class names into their Class objects.
610      * @return An array containing the Class object for each parameter. This
611      * array is in the same order as the given List and is suitable for passing
612      * to the validation method.
613      * @throws ValidatorException if a class cannot be loaded.
614      */

615     private void loadParameterClasses(ClassLoader JavaDoc loader)
616         throws ValidatorException {
617
618         if (this.parameterClasses != null) {
619             return;
620         }
621         
622         this.parameterClasses = new Class JavaDoc[this.methodParameterList.size()];
623
624         for (int i = 0; i < this.methodParameterList.size(); i++) {
625             String JavaDoc paramClassName = (String JavaDoc) this.methodParameterList.get(i);
626
627             try {
628                 this.parameterClasses[i] = loader.loadClass(paramClassName);
629                     
630             } catch (ClassNotFoundException JavaDoc e) {
631                 throw new ValidatorException(e.getMessage());
632             }
633         }
634     }
635     
636     /**
637      * Converts a List of parameter class names into their values contained in
638      * the parameters Map.
639      * @param params A Map of class names to parameter values.
640      * @return An array containing the value object for each parameter. This
641      * array is in the same order as the given List and is suitable for passing
642      * to the validation method.
643      */

644     private Object JavaDoc[] getParameterValues(Map JavaDoc params) {
645
646         Object JavaDoc[] paramValue = new Object JavaDoc[this.methodParameterList.size()];
647
648         for (int i = 0; i < this.methodParameterList.size(); i++) {
649             String JavaDoc paramClassName = (String JavaDoc) this.methodParameterList.get(i);
650             paramValue[i] = params.get(paramClassName);
651         }
652
653         return paramValue;
654     }
655     
656     /**
657      * Return an instance of the validation class or null if the validation
658      * method is static so does not require an instance to be executed.
659      */

660     private Object JavaDoc getValidationClassInstance() throws ValidatorException {
661         if (Modifier.isStatic(this.validationMethod.getModifiers())) {
662             this.instance = null;
663
664         } else {
665             if (this.instance == null) {
666                 try {
667                     this.instance = this.validationClass.newInstance();
668                 } catch (InstantiationException JavaDoc e) {
669                     String JavaDoc msg =
670                         "Couldn't create instance of "
671                             + this.classname
672                             + ". "
673                             + e.getMessage();
674
675                     throw new ValidatorException(msg);
676
677                 } catch (IllegalAccessException JavaDoc e) {
678                     String JavaDoc msg =
679                         "Couldn't create instance of "
680                             + this.classname
681                             + ". "
682                             + e.getMessage();
683
684                     throw new ValidatorException(msg);
685                 }
686             }
687         }
688
689         return this.instance;
690     }
691     
692     /**
693      * Modifies the paramValue array with indexed fields.
694      *
695      * @param field
696      * @param pos
697      * @param paramValues
698      */

699     private void handleIndexedField(Field field, int pos, Object JavaDoc[] paramValues)
700         throws ValidatorException {
701
702         int beanIndex = this.methodParameterList.indexOf(Validator.BEAN_PARAM);
703         int fieldIndex = this.methodParameterList.indexOf(Validator.FIELD_PARAM);
704
705         Object JavaDoc indexedList[] = field.getIndexedProperty(paramValues[beanIndex]);
706
707         // Set current iteration object to the parameter array
708
paramValues[beanIndex] = indexedList[pos];
709
710         // Set field clone with the key modified to represent
711
// the current field
712
Field indexedField = (Field) field.clone();
713         indexedField.setKey(
714             ValidatorUtils.replace(
715                 indexedField.getKey(),
716                 Field.TOKEN_INDEXED,
717                 "[" + pos + "]"));
718
719         paramValues[fieldIndex] = indexedField;
720     }
721     
722     /**
723      * If the result object is a <code>Boolean</code>, it will return its
724      * value. If not it will return <code>false</code> if the object is
725      * <code>null</code> and <code>true</code> if it isn't.
726      */

727     private boolean isValid(Object JavaDoc result) {
728         if (result instanceof Boolean JavaDoc) {
729             Boolean JavaDoc valid = (Boolean JavaDoc) result;
730             return valid.booleanValue();
731         } else {
732             return (result != null);
733         }
734     }
735
736     /**
737      * Returns the ClassLoader set in the Validator contained in the parameter
738      * Map.
739      */

740     private ClassLoader JavaDoc getClassLoader(Map JavaDoc params) {
741         Validator v = (Validator) params.get(Validator.VALIDATOR_PARAM);
742         return v.getClassLoader();
743     }
744     
745     /**
746      * Returns the onlyReturnErrors setting in the Validator contained in the
747      * parameter Map.
748      */

749     private boolean onlyReturnErrors(Map JavaDoc params) {
750         Validator v = (Validator) params.get(Validator.VALIDATOR_PARAM);
751         return v.getOnlyReturnErrors();
752     }
753
754 }
755
Popular Tags