View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/validation/DbFormsValidatorUtil.java,v 1.20 2004/10/11 08:55:05 hkollmann Exp $
3    * $Revision: 1.20 $
4    * $Date: 2004/10/11 08:55:05 $
5    *
6    * DbForms - a Rapid Application Development Framework
7    * Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation; either
12   * version 2.1 of the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22   */
23  
24  package org.dbforms.validation;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.commons.validator.Arg;
29  import org.apache.commons.validator.Field;
30  import org.apache.commons.validator.Form;
31  import org.apache.commons.validator.ValidatorAction;
32  import org.apache.commons.validator.ValidatorResources;
33  import org.apache.commons.validator.ValidatorUtil;
34  import org.apache.commons.validator.Var;
35  
36  import org.dbforms.config.Constants;
37  import org.dbforms.config.DbFormsErrors;
38  
39  import org.dbforms.util.MessageResources;
40  import org.dbforms.util.Util;
41  
42  import java.util.ArrayList;
43  import java.util.Collections;
44  import java.util.Comparator;
45  import java.util.Enumeration;
46  import java.util.Hashtable;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Locale;
50  import java.util.Map;
51  
52  
53  
54  /***********************************************************************************************
55   *         This class generate error string from ResourceBundle if enable,
56   *  else generate error in DbForms-Error.xml standard.
57   *
58   *  @author Eric Beaumier
59   **********************************************************************************************/
60  public class DbFormsValidatorUtil {
61     static Log logCat = LogFactory.getLog(DbFormsValidatorUtil.class.getName());
62  
63     /***********************************************************************************************
64      *  Retrieve <arg> parameter from validation.xml.
65      *
66      * @param  <code>String</code> : Type of validation (required, range, email,...).
67      * @param  <code>Locale</code> : Locale object to map with the good ResourceBundle
68      * @param  <code>Field</code> : Field validator information. (retrieve <arg_> tag from validation.xml).
69      *
70      *
71     * @return        <code>String</code> : Message resolve.
72      **********************************************************************************************/
73     public static String[] getArgs(String actionName,
74                                    Locale locale,
75                                    Field  field) {
76        Arg    arg0 = field.getArg0(actionName);
77        Arg    arg1 = field.getArg1(actionName);
78        Arg    arg2 = field.getArg2(actionName);
79        Arg    arg3 = field.getArg3(actionName);
80  
81        String sArg0 = null;
82        String sArg1 = null;
83        String sArg2 = null;
84        String sArg3 = null;
85  
86        if (arg0 != null) {
87           if (arg0.getResource()) {
88              sArg0 = MessageResources.getMessage(arg0.getKey(), locale);
89  
90              if (sArg0 == null) {
91                 sArg0 = arg0.getKey();
92              }
93           } else {
94              sArg0 = arg0.getKey();
95           }
96        } else {
97           sArg0 = field.getKey(); // If no <arg0> define
98        }
99  
100       if (arg1 != null) {
101          if (arg1.getResource()) {
102             sArg1 = MessageResources.getMessage(arg1.getKey(), locale);
103 
104             if (sArg1 == null) {
105                sArg1 = arg1.getKey();
106             }
107          } else {
108             sArg1 = arg1.getKey();
109          }
110       }
111 
112       if (arg2 != null) {
113          if (arg2.getResource()) {
114             sArg2 = MessageResources.getMessage(arg2.getKey(), locale);
115 
116             if (sArg2 == null) {
117                sArg2 = arg2.getKey();
118             }
119          } else {
120             sArg2 = arg2.getKey();
121          }
122       }
123 
124       if (arg3 != null) {
125          if (arg3.getResource()) {
126             sArg3 = MessageResources.getMessage(arg3.getKey(), locale);
127 
128             if (sArg3 == null) {
129                sArg3 = arg3.getKey();
130             }
131          } else {
132             sArg3 = arg3.getKey();
133          }
134       }
135 
136       return new String[] {
137                 sArg0,
138                 sArg1,
139                 sArg2,
140                 sArg3
141              };
142    }
143 
144 
145    /***********************************************************************************************
146     *  J A V S C R I P T   G E N E R A T I O N.
147     *
148     * @param  <code>formName</code> : Name to match with <form> in validation.xml.
149     * @param  <code>Locale</code> : Locale object to map with the ResourceBundle
150     * @param  <code>Hashtable</code> : Hashtable of field name in table and generated name from DbForms (ex: "field1" : "f_4_3@root_4").
151     * @param  <code>ValidatorResources</code> : ValidatorResources use by DbForms
152     * @param  <code>javascriptValidationSrcFile</code> : Validation file name to include in src of script.  <SCRIPT src="..."></SCRIPT>
153     * @param  <code>DbFormsErrors</code> : Class to resolve DbForms-Error format (DbForms-Errors.xml).
154     *
155    * @return        <code>StringBuffer</code> : The complete javascript validation section to include in response.
156     **********************************************************************************************/
157    public static StringBuffer getJavascript(List               formNames,
158                                             Locale             locale,
159                                             Hashtable          fieldsName,
160                                             ValidatorResources resources,
161                                             String             javascriptValidationSrcFile,
162                                             DbFormsErrors      errors) {
163       StringBuffer results            = new StringBuffer(); // Final result.   All the Javascript validation code
164       StringBuffer jsFunctions        = new StringBuffer(); // Only the validateFunctions() definition from ValidatorAction
165       boolean      bJavascriptSrcFile = (javascriptValidationSrcFile != null);
166 
167       Form         form     = null;
168       String       formName = "";
169 
170       // Iterate through all forms in formName array
171       List lActions       = new ArrayList();
172       List lActionMethods = new ArrayList();
173 
174       for (Iterator formIterator = formNames.iterator();
175                  formIterator.hasNext();) {
176          formName = (String) formIterator.next();
177 
178          if ((form = resources.get(locale, formName)) != null) {
179             // Get List of actions for this Form
180             for (Iterator i = form.getFields()
181                                   .iterator(); i.hasNext();) {
182                Field field = (Field) i.next();
183 
184                // Select only fields inside the <db:form >...</db:form>
185                //if(!fieldsName.containsKey(field.getProperty())) continue;
186                if (!fieldsName.containsValue(field.getProperty())) {
187                   continue;
188                }
189 
190                for (Iterator x = field.getDependencies()
191                                       .iterator(); x.hasNext();) {
192                   Object o = x.next();
193 
194                   if ((o != null) && !lActionMethods.contains(o)) {
195                      lActionMethods.add(o);
196                   }
197                }
198             }
199          }
200       }
201 
202       // Create list of ValidatorActions based on lActionMethods
203       for (Iterator i = lActionMethods.iterator(); i.hasNext();) {
204          String          actionName = (String) i.next();
205          ValidatorAction va = resources.getValidatorAction(actionName);
206 
207          if (va != null) {
208             String javascript = va.getJavascript();
209 
210             if ((javascript != null) && (javascript.length() > 0)) {
211                lActions.add(va);
212             } else {
213                i.remove();
214             }
215          } else {
216             i.remove();
217          }
218       }
219 
220       Collections.sort(lActions,
221                        new Comparator() {
222             public int compare(Object o1,
223                                Object o2) {
224                ValidatorAction va1 = (ValidatorAction) o1;
225                ValidatorAction va2 = (ValidatorAction) o2;
226 
227                if (((va1.getDepends() == null)
228                          || (va1.getDepends()
229                                       .length() == 0))
230                          && ((va2.getDepends() == null)
231                          || (va2.getDepends()
232                                       .length() == 0))) {
233                   return 0;
234                } else if (((va1.getDepends() != null)
235                                 && (va1.getDepends()
236                                              .length() > 0))
237                                 && ((va2.getDepends() == null)
238                                 || (va2.getDepends()
239                                              .length() == 0))) {
240                   return 1;
241                } else if (((va1.getDepends() == null)
242                                 || (va1.getDepends()
243                                              .length() == 0))
244                                 && ((va2.getDepends() != null)
245                                 && (va2.getDepends()
246                                              .length() > 0))) {
247                   return -1;
248                } else {
249                   return va1.getDependencies()
250                             .size() - va2.getDependencies()
251                                          .size();
252                }
253             }
254          });
255 
256       String methods = null;
257 
258       for (Iterator i = lActions.iterator(); i.hasNext();) {
259          ValidatorAction va = (ValidatorAction) i.next();
260 
261          if (methods == null) {
262             methods = va.getMethod() + "(form)";
263          } else {
264             methods += (" && " + va.getMethod() + "(form)");
265          }
266       }
267 
268       results.append(getJavascriptBegin(formName, methods));
269 
270       for (Iterator i = lActions.iterator(); i.hasNext();) {
271          ValidatorAction va           = (ValidatorAction) i.next();
272          String          jscriptVar   = null;
273          String          functionName = null;
274 
275          if ((va.getJsFunctionName() != null)
276                    && (va.getJsFunctionName()
277                                .length() > 0)) {
278             functionName = va.getJsFunctionName();
279          } else {
280             functionName = va.getName();
281          }
282 
283          if (!bJavascriptSrcFile) {
284             jsFunctions.append(va.getJavascript());
285             jsFunctions.append("\n\n");
286          }
287 
288          results.append("	 function " + functionName + " () { \n");
289 
290          for (Iterator formIterator = formNames.iterator();
291                     formIterator.hasNext();) {
292             formName = (String) formIterator.next();
293 
294             if ((form = resources.get(locale, formName)) != null) {
295                for (Iterator x = form.getFields()
296                                      .iterator(); x.hasNext();) {
297                   Field field = (Field) x.next();
298 
299                   // Skip indexed fields for now until there is 
300                   // a good way to handle error messages (and the length of the list (could retrieve from scope?))
301                   if (!field.isIndexed() && field.isDependency(va.getName())) {
302                      String      message = getMessage(functionName, va, locale,
303                                                       field, errors);
304                      Enumeration e = fieldsName.keys();
305 
306                      while (e.hasMoreElements()) {
307                         String fieldName = (String) e.nextElement();
308                         String val = (String) fieldsName.get(fieldName);
309 
310                         if (field.getKey()
311                                        .equals(val)) {
312                            jscriptVar = getNextVar(jscriptVar);
313 
314                            /*
315                                  Grunikiewicz.philip@hydro.qc.ca
316                                  2004-01-22
317                                  To support subforms, needed to replace insroot by ins
318                            */
319                            if (fieldName.indexOf(Constants.FIELDNAME_INSERTPREFIX) != -1) {
320                               // Valide only Insert Mode
321                               results.append("\t    if(")
322                                      .append(ValidatorConstants.JS_UPDATE_VALIDATION_MODE)
323                                      .append("==false) ");
324                            } else {
325                               // Valide only Update Mode
326                               results.append("\t    if(")
327                                      .append(ValidatorConstants.JS_UPDATE_VALIDATION_MODE)
328                                      .append("==true) ");
329                            }
330 
331                            results.append(" this.dbforms_" + jscriptVar
332                                           + " = new Array(\"" + fieldName
333                                           + "\", \"" + message + "\", ");
334                            results.append("new Function (\"varName\", \"");
335 
336                            Map hVars = field.getVars();
337 
338                            // Loop through the field's variables.
339                            for (Iterator iVars = hVars.keySet()
340                                                       .iterator();
341                                       iVars.hasNext();) {
342                               String varKey   = (String) iVars.next();
343                               Var    var      = (Var) hVars.get(varKey);
344                               String varValue = var.getValue();
345                               String jsType   = var.getJsType();
346 
347                               if (Var.JSTYPE_INT.equalsIgnoreCase(jsType)) {
348                                  results.append("this." + varKey + "="
349                                                 + ValidatorUtil.replace(varValue,
350                                                                         "//",
351                                                                         "////")
352                                                 + "; ");
353                               } else if (Var.JSTYPE_REGEXP.equalsIgnoreCase(jsType)) {
354                                  results.append("this." + varKey + "=/"
355                                                 + ValidatorUtil.replace(varValue,
356                                                                         "//",
357                                                                         "////")
358                                                 + "/; ");
359                               } else if (Var.JSTYPE_STRING.equalsIgnoreCase(jsType)) {
360                                  results.append("this." + varKey + "='"
361                                                 + ValidatorUtil.replace(varValue,
362                                                                         "//",
363                                                                         "////")
364                                                 + "'; ");
365                               }
366                               // So everyone using the latest format doesn't need to change their xml files immediately.
367                               else if ("mask".equalsIgnoreCase(varKey)) {
368                                  results.append("this." + varKey + "=/"
369                                                 + ValidatorUtil.replace(varValue,
370                                                                         "//",
371                                                                         "////")
372                                                 + "/; ");
373                               } else {
374                                  results.append("this." + varKey + "='"
375                                                 + ValidatorUtil.replace(varValue,
376                                                                         "//",
377                                                                         "////")
378                                                 + "'; ");
379                               }
380                            }
381 
382                            results.append(" return this[varName];\"));\n");
383                         }
384                      }
385                   }
386                }
387             }
388          }
389 
390          results.append("	 } \n\n");
391       }
392 
393       // 2003-01-30 HKK: if form not found do not generte trailing script code!!!        
394       if (!bJavascriptSrcFile) {
395          results.append(jsFunctions.toString());
396       }
397 
398       results.append(getJavascriptEnd());
399 
400       if (bJavascriptSrcFile) {
401          results.append("\n<SCRIPT language=\"javascript\" src=\""
402                         + javascriptValidationSrcFile + "\"></SCRIPT> \n");
403       }
404 
405       return results;
406    }
407 
408 
409    /***********************************************************************************************
410     *  Get the error message from ResourceBundle if present, else generate a DbForms-Error.xml standard.
411     *
412     * @param  <code>String</code> : Type of validation (required, range, email,...).
413     * @param  <code>ValidatorAction</code> : ValidatorAction to retrieve validation definition.
414     * @param  <code>Locale</code> : Locale object to map with the good ResourceBundle
415     * @param  <code>Field</code> : Field validator information. (retrieve <arg_> tag from validation.xml).
416     * @param  <code>DbFormsErrors</code> : DbForms Error class to retrieve error message in DbForm-Errors.xml format.
417     *
418    * @return        <code>String</code> : Message resolve.
419     **********************************************************************************************/
420    public static String getMessage(String          type,
421                                    ValidatorAction va,
422                                    Locale          locale,
423                                    Field           field,
424                                    DbFormsErrors   errors) {
425       String   result = null;
426       String[] arg = getArgs(va.getName(), locale, field);
427       String   msg = ((field.getMsg(va.getName()) != null)
428                       ? field.getMsg(va.getName())
429                       : va.getMsg());
430 
431       if (msg == null) {
432          msg = "errors." + va.getName();
433       }
434 
435       //***************************************************
436       // Try to resolve message with Application Resource
437       //**************************************************
438       result = MessageResources.getMessage(msg, locale, arg);
439 
440       if (result != null) {
441          return result;
442       }
443 
444       //**************************************************	
445       // ELSE Generate DbForms-Error.xml standard	
446       //**************************************************
447       result = msg + ":";
448 
449       for (int i = 0; i < arg.length; i++) {
450          if (arg[i] != null) {
451             result += (arg[i] + ",");
452          }
453       }
454 
455       // 2003-01-30 HKK: Removed trailing ,
456       result = result.substring(0, result.length() - 1);
457 
458       // 2002-09-12 HKK: Catch errors while getXMLErrorMessage!
459       try {
460          result = errors.getXMLErrorMessage(result);
461       } catch (Exception e) {
462          logCat.error("Not in proper format - do not try to convert!");
463       }
464 
465       return result;
466    }
467 
468 
469    //*********************************************************************************************
470    // ***
471    // ***  P R I V A T E    U S E 
472    // ***
473    //*********************************************************************************************
474    private static String getJavascriptBegin(String formName,
475                                             String methods) {
476       StringBuffer sb   = new StringBuffer();
477       String       name = Character.toUpperCase(formName.charAt(0))
478                           + formName.substring(1, formName.length());
479 
480       sb.append("<script language=\"Javascript1.1\"> \n");
481 
482       sb.append("<!-- Begin \n");
483       sb.append("\n	 var " + ValidatorConstants.JS_CANCEL_VALIDATION
484                 + " = false;");
485       sb.append("\n	 var " + ValidatorConstants.JS_UPDATE_VALIDATION_MODE
486                 + " = true; \n\n");
487 
488       sb.append("	 function validate" + name + "(form) {  \n");
489 
490       sb.append("	      if (!" + ValidatorConstants.JS_CANCEL_VALIDATION
491                 + ") \n");
492       sb.append("	 	return true; \n");
493       sb.append("	      else \n");
494 
495       // Always return true if there aren't any Javascript validation methods
496       if (Util.isNull(methods)) {
497          sb.append("	 	return true; \n");
498       } else {
499          sb.append("	 	return " + methods + "; \n");
500       }
501 
502       sb.append("	 } \n\n");
503 
504       return sb.toString();
505    }
506 
507 
508    private static String getJavascriptEnd() {
509       StringBuffer sb = new StringBuffer();
510 
511       sb.append("\n");
512       sb.append("//  End -->\n");
513       sb.append("</script>\n\n");
514 
515       return sb.toString();
516    }
517 
518 
519    private static String getNextVar(String input) {
520       if (input == null) {
521          return "aa";
522       }
523 
524       input = input.toLowerCase();
525 
526       for (int i = input.length(); i > 0; i--) {
527          int  pos = i - 1;
528 
529          char c = input.charAt(pos);
530          c++;
531 
532          if (c <= 'z') {
533             if (i == 0) {
534                return c + input.substring(pos, input.length());
535             } else if (i == input.length()) {
536                return input.substring(0, pos) + c;
537             } else {
538                return input.substring(0, pos) + c
539                       + input.substring(pos, input.length() - 1);
540             }
541          } else {
542             input = replaceChar(input, pos, 'a');
543          }
544       }
545 
546       return null;
547    }
548 
549 
550    private static String replaceChar(String input,
551                                      int    pos,
552                                      char   c) {
553       if (pos == 0) {
554          return c + input.substring(pos, input.length());
555       } else if (pos == input.length()) {
556          return input.substring(0, pos) + c;
557       } else {
558          return input.substring(0, pos) + c
559                 + input.substring(pos, input.length() - 1);
560       }
561    }
562 }