Coverage Report - org.kuali.rice.krad.uif.util.ClientValidationUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
ClientValidationUtils
8%
25/296
3%
9/244
7.25
ClientValidationUtils$ValidationMessageKeys
0%
0/14
0%
0/4
7.25
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krad.uif.util;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.core.api.config.property.ConfigurationService;
 20  
 import org.kuali.rice.krad.datadictionary.validation.constraint.BaseConstraint;
 21  
 import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
 22  
 import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint;
 23  
 import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
 24  
 import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
 25  
 import org.kuali.rice.krad.datadictionary.validation.constraint.SimpleConstraint;
 26  
 import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
 27  
 import org.kuali.rice.krad.datadictionary.validation.constraint.WhenConstraint;
 28  
 import org.kuali.rice.krad.service.KRADServiceLocator;
 29  
 import org.kuali.rice.krad.uif.UifConstants;
 30  
 import org.kuali.rice.krad.uif.field.InputField;
 31  
 import org.kuali.rice.krad.uif.view.View;
 32  
 import org.kuali.rice.krad.uif.control.TextControl;
 33  
 
 34  
 import java.text.MessageFormat;
 35  
 import java.util.ArrayList;
 36  
 import java.util.EnumSet;
 37  
 import java.util.List;
 38  
 
 39  
 /**
 40  
  * This class contains all the methods necessary for generating the js required to perform validation client side.
 41  
  * The processAndApplyConstraints(InputField field, View view) is the key method of this class used by
 42  
  * InputField to setup its client side validation mechanisms.
 43  
  * 
 44  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 45  
  */
 46  0
 public class ClientValidationUtils {
 47  
         // used to give validation methods unique signatures
 48  1
         private static int methodKey = 0;
 49  
         
 50  
         // list used to temporarily store mustOccurs field names for the error
 51  
         // message
 52  
         private static List<List<String>> mustOccursPathNames;
 53  
         
 54  
         public static final String LABEL_KEY_SPLIT_PATTERN = ",";
 55  
         
 56  
         
 57  
         public static final String PREREQ_MSG_KEY = "prerequisite";
 58  
         public static final String POSTREQ_MSG_KEY = "postrequisite";
 59  
         public static final String MUSTOCCURS_MSG_KEY = "mustOccurs";
 60  
         public static final String GENERIC_FIELD_MSG_KEY = "general.genericFieldName";
 61  
         
 62  
         public static final String ALL_MSG_KEY = "general.all";
 63  
         public static final String ATMOST_MSG_KEY = "general.atMost";
 64  
         public static final String AND_MSG_KEY = "general.and";
 65  
         public static final String OR_MSG_KEY = "general.or";
 66  
         
 67  1
         private static ConfigurationService configService = KRADServiceLocator.getKualiConfigurationService();
 68  
         
 69  
         //Enum representing names of rules provided by the jQuery plugin
 70  0
         public static enum ValidationMessageKeys{
 71  0
                 REQUIRED("required"), 
 72  0
                 MIN_EXCLUSIVE("minExclusive"), 
 73  0
                 MAX_INCLUSIVE("maxInclusive"),
 74  0
                 MIN_LENGTH("minLengthConditional"),
 75  0
                 MAX_LENGTH("maxLengthConditional");
 76  
                 
 77  0
                 private ValidationMessageKeys(String name) {
 78  0
                         this.name = name;
 79  0
                 }
 80  
                 
 81  
                 private final String name;
 82  
                 
 83  
                 @Override
 84  
                 public String toString() {
 85  0
                         return name;
 86  
                 }
 87  
                 
 88  
                 public static boolean contains(String name){
 89  0
             for (ValidationMessageKeys element : EnumSet.allOf(ValidationMessageKeys.class)) {
 90  0
                 if (element.toString().equalsIgnoreCase(name)) {
 91  0
                     return true;
 92  
                 }
 93  
             }
 94  0
             return false;
 95  
                 }
 96  
         }
 97  
         
 98  
         public static String generateMessageFromLabelKey(List<String> params, String labelKey){
 99  3
                 String message = "NO MESSAGE";
 100  3
                 if(StringUtils.isNotEmpty(labelKey)){
 101  0
                         message = configService.getPropertyValueAsString(labelKey);
 102  0
                         if(params != null && !params.isEmpty() && StringUtils.isNotEmpty(message)){
 103  0
                             message = MessageFormat.format(message, params.toArray());
 104  
                         }
 105  
                 }
 106  3
                 if(StringUtils.isEmpty(message)){
 107  0
                     message = labelKey;
 108  
                 }
 109  
                 //replace characters that might cause issues with their equivalent html codes
 110  3
                 if(message.contains("\"")){
 111  0
                     message = message.replace("\"", "&quot;");
 112  
                 }
 113  3
                 if(message.contains("'")){
 114  0
                     message = message.replace("'", "&#39;");
 115  
                 }
 116  3
                 if(message.contains("\\")){
 117  0
                     message = message.replace("\\", "&#92;");
 118  
                 }
 119  3
                 return message;
 120  
         }
 121  
 
 122  
         /**
 123  
          * Generates the js object used to override all default messages for validator jquery plugin with custom
 124  
          * messages derived from the configService.
 125  
          * 
 126  
          * @return
 127  
          */
 128  
         public static String generateValidatorMessagesOption(){
 129  0
                 String mOption = "";
 130  0
                 String keyValuePairs = "";
 131  0
                 for(ValidationMessageKeys element : EnumSet.allOf(ValidationMessageKeys.class)){
 132  0
                         String key = element.toString();
 133  0
                         String message = configService.getPropertyValueAsString(UifConstants.Messages.VALIDATION_MSG_KEY_PREFIX + key);
 134  0
                         if(StringUtils.isNotEmpty(message)){
 135  0
                                 keyValuePairs = keyValuePairs + "\n" + key + ": '"+ message + "',";
 136  
                                 
 137  
                         }
 138  
                         
 139  0
                 }
 140  0
                 keyValuePairs = StringUtils.removeEnd(keyValuePairs, ",");
 141  0
                 if(StringUtils.isNotEmpty(keyValuePairs)){
 142  0
                         mOption="{" + keyValuePairs + "}";
 143  
                 }
 144  
                 
 145  0
                 return mOption;
 146  
         }
 147  
         
 148  
         /**
 149  
          * Returns the add method jquery validator call for the regular expression
 150  
          * stored in validCharactersConstraint.
 151  
          * 
 152  
          * @param validCharactersConstraint
 153  
          * @return js validator.addMethod script
 154  
          */
 155  
         public static String getRegexMethod(InputField field, ValidCharactersConstraint validCharactersConstraint) {
 156  1
                 String message = generateMessageFromLabelKey(validCharactersConstraint.getValidationMessageParams(), validCharactersConstraint.getLabelKey());
 157  1
                 String key = "validChar-" + field.getBindingInfo().getBindingPath() + methodKey;
 158  
                 
 159  1
                 String regex = validCharactersConstraint.getValue();
 160  
                 //replace characters known to cause issues if not escaped
 161  1
                 if(regex.contains("\\\\")){
 162  1
                     regex = regex.replaceAll("\\\\", "\\\\\\\\");
 163  
                 }
 164  1
                 if(regex.contains("/")){
 165  1
                     regex = regex.replace("/", "\\/");
 166  
                 }
 167  
                 
 168  1
         return "\njQuery.validator.addMethod(\"" + key
 169  
                 + "\", function(value, element) {\n "
 170  
                 + "return this.optional(element) || /" + regex + "/.test(value);"
 171  
                 + "}, \"" + message + "\");";
 172  
         }
 173  
         
 174  
         /**
 175  
      * Returns the add method jquery validator call for the regular expression
 176  
      * stored in validCharactersConstraint that explicitly checks a boolean.  Needed because one method
 177  
      * accepts params and the other doesn't.
 178  
      * 
 179  
      * @param validCharactersConstraint
 180  
      * @return js validator.addMethod script
 181  
      */
 182  
     public static String getRegexMethodWithBooleanCheck(InputField field, ValidCharactersConstraint validCharactersConstraint) {
 183  1
         String message = generateMessageFromLabelKey(validCharactersConstraint.getValidationMessageParams(), validCharactersConstraint.getLabelKey());
 184  1
         String key = "validChar-" + field.getBindingInfo().getBindingPath() + methodKey;
 185  
         
 186  1
         String regex = validCharactersConstraint.getValue();
 187  
         //replace characters known to cause issues if not escaped
 188  1
         if(regex.contains("\\\\")){
 189  1
             regex = regex.replaceAll("\\\\", "\\\\\\\\");
 190  
         }
 191  1
         if(regex.contains("/")){
 192  1
                     regex = regex.replace("/", "\\/");
 193  
                 }
 194  
         
 195  1
         return "\njQuery.validator.addMethod(\"" + key
 196  
                 + "\", function(value, element, doCheck) {\n if(doCheck === false){return true;}else{"
 197  
                 + "return this.optional(element) || /" + regex + "/.test(value);}"
 198  
                 + "}, \"" + message + "\");";
 199  
     }
 200  
         
 201  
 
 202  
         /**
 203  
          * This method processes a single CaseConstraint. Internally it makes calls
 204  
          * to processWhenConstraint for each WhenConstraint that exists in this
 205  
          * constraint. It adds a "dependsOn" css class to this field for the field
 206  
          * which the CaseConstraint references.
 207  
          * 
 208  
          * @param view
 209  
          * @param andedCase
 210  
          *            the boolean logic to be anded when determining if this case is
 211  
          *            satisfied (used for nested CaseConstraints)
 212  
          */
 213  
         public static void processCaseConstraint(InputField field, View view, CaseConstraint constraint, String andedCase) {
 214  0
                 if (constraint.getOperator() == null) {
 215  0
                         constraint.setOperator("equals");
 216  
                 }
 217  
 
 218  0
                 String operator = "==";
 219  0
                 if (constraint.getOperator().equalsIgnoreCase("not_equals")) {
 220  0
                         operator = "!=";
 221  
                 }
 222  0
                 else if (constraint.getOperator().equalsIgnoreCase("greater_than_equal")) {
 223  0
                         operator = ">=";
 224  
                 }
 225  0
                 else if (constraint.getOperator().equalsIgnoreCase("less_than_equal")) {
 226  0
                         operator = "<=";
 227  
                 }
 228  0
                 else if (constraint.getOperator().equalsIgnoreCase("greater_than")) {
 229  0
                         operator = ">";
 230  
                 }
 231  0
                 else if (constraint.getOperator().equalsIgnoreCase("less_than")) {
 232  0
                         operator = "<";
 233  
                 }
 234  0
                 else if (constraint.getOperator().equalsIgnoreCase("has_value")) {
 235  0
                         operator = "";
 236  
                 }
 237  
                 // add more operator types here if more are supported later
 238  
 
 239  0
                 field.getControl().addStyleClass("dependsOn-" + constraint.getPropertyName());
 240  
 
 241  0
                 if (constraint.getWhenConstraint() != null && !constraint.getWhenConstraint().isEmpty()) {
 242  0
                         String fieldPath = field.getBindingInfo().getBindingObjectPath() + "." + constraint.getPropertyName();
 243  0
                     for (WhenConstraint wc : constraint.getWhenConstraint()) {
 244  0
                                 processWhenConstraint(field, view, constraint, wc, fieldPath, operator, andedCase);
 245  
                         }
 246  
                 }
 247  0
         }
 248  
         
 249  
 
 250  
 
 251  
         /**
 252  
          * This method processes the WhenConstraint passed in. The when constraint
 253  
          * is used to create a boolean statement to determine if the constraint will
 254  
          * be applied. The necessary rules/methods for applying this constraint are
 255  
          * created in the createRule call. Note the use of the use of coerceValue js
 256  
          * function call.
 257  
          * 
 258  
          * @param view
 259  
          * @param wc
 260  
          * @param fieldPath
 261  
          * @param operator
 262  
          * @param andedCase
 263  
          */
 264  
         private static void processWhenConstraint(InputField field, View view, CaseConstraint caseConstraint, WhenConstraint wc, String fieldPath,
 265  
                         String operator, String andedCase) {
 266  0
                 String ruleString = "";
 267  
                 // prerequisite constraint
 268  
 
 269  0
                 String booleanStatement = "";
 270  0
                 if (wc.getValues() != null) {
 271  
 
 272  0
                         String caseStr = "";
 273  0
                         if (!caseConstraint.isCaseSensitive()) {
 274  0
                                 caseStr = ".toUpperCase()";
 275  
                         }
 276  0
                         for (int i = 0; i < wc.getValues().size(); i++) {
 277  0
                                 if (operator.isEmpty()) {
 278  
                                         // has_value case
 279  0
                                         if (wc.getValues().get(i) instanceof String
 280  
                                                         && ((String) wc.getValues().get(i)).equalsIgnoreCase("false")) {
 281  0
                                                 booleanStatement = booleanStatement + "(coerceValue('" + fieldPath + "') == '')";
 282  
                                         }
 283  
                                         else {
 284  0
                                                 booleanStatement = booleanStatement + "(coerceValue('" + fieldPath + "') != '')";
 285  
                                         }
 286  
                                 }
 287  
                                 else {
 288  
                                         // everything else
 289  0
                                         booleanStatement = booleanStatement + "(coerceValue('" + fieldPath + "')" + caseStr + " "
 290  
                                                         + operator + " \"" + wc.getValues().get(i) + "\"" + caseStr + ")";
 291  
                                 }
 292  0
                                 if ((i + 1) != wc.getValues().size()) {
 293  0
                                         booleanStatement = booleanStatement + " || ";
 294  
                                 }
 295  
                         }
 296  
 
 297  
                 }
 298  
 
 299  0
                 if (andedCase != null) {
 300  0
                         booleanStatement = "(" + booleanStatement + ") && (" + andedCase + ")";
 301  
                 }
 302  
 
 303  0
                 if (wc.getConstraint() != null && StringUtils.isNotEmpty(booleanStatement)) {
 304  0
                         ruleString = createRule(field, wc.getConstraint(), booleanStatement, view);
 305  
                 }
 306  
 
 307  0
                 if (StringUtils.isNotEmpty(ruleString)) {
 308  0
                         addScriptToPage(view, field, ruleString);
 309  
                 }
 310  0
         }
 311  
         
 312  
 
 313  
 
 314  
         /**
 315  
          * Adds the script to the view to execute on a jQuery document ready event.
 316  
          * 
 317  
          * @param view
 318  
          * @param script
 319  
          */
 320  
         public static void addScriptToPage(View view, InputField field, String script) {
 321  0
         String prefixScript = "";
 322  
         
 323  0
         if (field.getOnDocumentReadyScript() != null) {
 324  0
             prefixScript = field.getOnDocumentReadyScript();
 325  
         }
 326  0
         field.setOnDocumentReadyScript(prefixScript + "\n" + "runValidationScript(function(){" + script + "});");
 327  0
         }
 328  
         
 329  
         /**
 330  
          * Determines which fields are being evaluated in a boolean statement, so handlers can be
 331  
          * attached to them if needed, returns these names in a list.
 332  
          * 
 333  
          * @param statement
 334  
          * @return
 335  
          */
 336  
         private static List<String> parseOutFields(String statement){
 337  0
             List<String> fieldNames = new ArrayList<String>();
 338  0
             String[] splits = StringUtils.splitByWholeSeparator(statement, "coerceValue('");
 339  0
             for(String s: splits){
 340  0
                 String fieldName = StringUtils.substringBefore(s, "'");
 341  0
                 fieldNames.add(fieldName);
 342  
             }
 343  0
             return fieldNames;
 344  
         }
 345  
 
 346  
         /**
 347  
          * This method takes in a constraint to apply only when the passed in
 348  
          * booleanStatement is valid. The method will create the necessary addMethod
 349  
          * and addRule jquery validator calls for the rule to be applied to the
 350  
          * field when the statement passed in evaluates to true during runtime and
 351  
          * this field is being validated. Note the use of custom methods for min/max
 352  
          * length/value.
 353  
          * 
 354  
          * @param field
 355  
          *            the field to apply the generated methods and rules to
 356  
          * @param constraint
 357  
          *            the constraint to be applied when the booleanStatement
 358  
          *            evaluates to true during validation
 359  
          * @param booleanStatement
 360  
          *            the booleanstatement in js - should return true when the
 361  
          *            validation rule should be applied
 362  
          * @param view
 363  
          * @return
 364  
          */
 365  
         @SuppressWarnings("boxing")
 366  
         private static String createRule(InputField field, Constraint constraint, String booleanStatement, View view) {
 367  0
                 String rule = "";
 368  0
                 int constraintCount = 0;
 369  0
                 if (constraint instanceof BaseConstraint && ((BaseConstraint) constraint).getApplyClientSide()) {
 370  0
                         if (constraint instanceof SimpleConstraint) {
 371  0
                                 if (((SimpleConstraint) constraint).getRequired() != null && ((SimpleConstraint) constraint).getRequired()) {
 372  0
                                         rule = rule + "required: function(element){\nreturn (" + booleanStatement + ");}";
 373  
                                         //special requiredness indicator handling
 374  0
                                         String showIndicatorScript = "";
 375  0
                                         for(String checkedField: parseOutFields(booleanStatement)){
 376  0
                                             showIndicatorScript = showIndicatorScript + 
 377  
                                                 "setupShowReqIndicatorCheck('"+ checkedField +"', '" + field.getBindingInfo().getBindingPath() + "', " + "function(){\nreturn (" + booleanStatement + ");});\n";
 378  
                                         }
 379  0
                                         addScriptToPage(view, field, showIndicatorScript);
 380  
                                         
 381  0
                                         constraintCount++;
 382  
                                 }
 383  0
                                 if (((SimpleConstraint) constraint).getMinLength() != null) {
 384  0
                                         if (constraintCount > 0) {
 385  0
                                                 rule = rule + ",\n";
 386  
                                         }
 387  0
                                         rule = rule + "minLengthConditional: [" + ((SimpleConstraint) constraint).getMinLength()
 388  
                                                         + ", function(){return " + booleanStatement + ";}]";
 389  0
                                         constraintCount++;
 390  
                                 }
 391  0
                                 if (((SimpleConstraint) constraint).getMaxLength() != null) {
 392  0
                                         if (constraintCount > 0) {
 393  0
                                                 rule = rule + ",\n";
 394  
                                         }
 395  0
                                         rule = rule + "maxLengthConditional: [" + ((SimpleConstraint) constraint).getMaxLength()
 396  
                                                         + ", function(){return " + booleanStatement + ";}]";
 397  0
                                         constraintCount++;
 398  
                                 }
 399  
 
 400  0
                                 if (((SimpleConstraint) constraint).getExclusiveMin() != null) {
 401  0
                                         if (constraintCount > 0) {
 402  0
                                                 rule = rule + ",\n";
 403  
                                         }
 404  0
                                         rule = rule + "minExclusive: [" + ((SimpleConstraint) constraint).getExclusiveMin()
 405  
                                                         + ", function(){return " + booleanStatement + ";}]";
 406  0
                                         constraintCount++;
 407  
                                 }
 408  
 
 409  0
                                 if (((SimpleConstraint) constraint).getInclusiveMax() != null) {
 410  0
                                         if (constraintCount > 0) {
 411  0
                                                 rule = rule + ",\n";
 412  
                                         }
 413  0
                                         rule = rule + "maxInclusive: [" + ((SimpleConstraint) constraint).getInclusiveMax()
 414  
                                                         + ", function(){return " + booleanStatement + ";}]";
 415  0
                                         constraintCount++;
 416  
                                 }
 417  
 
 418  0
                                 rule = "jq('[name=\"" + field.getBindingInfo().getBindingPath() + "\"]').rules(\"add\", {" + rule + "\n});";
 419  
                         }
 420  0
                         else if (constraint instanceof ValidCharactersConstraint) {
 421  0
                                 String regexMethod = "";
 422  0
                                 String methodName = "";
 423  0
                                 if(StringUtils.isNotEmpty(((ValidCharactersConstraint)constraint).getValue())) {
 424  0
                                         regexMethod = ClientValidationUtils.getRegexMethodWithBooleanCheck(field, (ValidCharactersConstraint) constraint) + "\n";
 425  0
                                         methodName = "validChar-" + field.getBindingInfo().getBindingPath() + methodKey;
 426  0
                                         methodKey++;
 427  
                                 }
 428  
                                 else {
 429  0
                                         if(StringUtils.isNotEmpty(((ValidCharactersConstraint)constraint).getLabelKey())){
 430  0
                                                 methodName = ((ValidCharactersConstraint)constraint).getLabelKey();
 431  
                                         }
 432  
                                 }
 433  0
                                 if (StringUtils.isNotEmpty(methodName)) {
 434  0
                                         rule = regexMethod + "jq('[name=\"" + field.getBindingInfo().getBindingPath() + "\"]').rules(\"add\", {\n\"" + methodName
 435  
                                                         + "\" : function(element){return (" + booleanStatement + ");}\n});";
 436  
                                 }
 437  0
                         }
 438  0
                         else if (constraint instanceof PrerequisiteConstraint) {
 439  0
                                 processPrerequisiteConstraint(field, (PrerequisiteConstraint) constraint, view, booleanStatement);
 440  
                         }
 441  0
                         else if (constraint instanceof CaseConstraint) {
 442  0
                                 processCaseConstraint(field, view, (CaseConstraint) constraint, booleanStatement);
 443  
                         }
 444  0
                         else if (constraint instanceof MustOccurConstraint) {
 445  0
                                 processMustOccurConstraint(field, view, (MustOccurConstraint) constraint, booleanStatement);
 446  
                         }
 447  
                 }
 448  0
                 return rule;
 449  
         }
 450  
         
 451  
 
 452  
 
 453  
         /**
 454  
          * This method is a simpler version of processPrerequisiteConstraint
 455  
          * 
 456  
          * @see ClientValidationUtils#processPrerequisiteConstraint(org.kuali.rice.krad.uif.field.InputField, PrerequisiteConstraint, View, String)
 457  
          * @param constraint
 458  
          * @param view
 459  
          */
 460  
         public static void processPrerequisiteConstraint(InputField field, PrerequisiteConstraint constraint, View view) {
 461  0
                 processPrerequisiteConstraint(field, constraint, view, "true");
 462  0
         }
 463  
 
 464  
         /**
 465  
          * This method processes a Prerequisite constraint that should be applied
 466  
          * when the booleanStatement passed in evaluates to true.
 467  
          * 
 468  
          * @param constraint
 469  
          *            prerequisiteConstraint
 470  
          * @param view
 471  
          * @param booleanStatement
 472  
          *            the booleanstatement in js - should return true when the
 473  
          *            validation rule should be applied
 474  
          */
 475  
         public static void processPrerequisiteConstraint(InputField field, PrerequisiteConstraint constraint, View view, String booleanStatement) {
 476  0
                 if (constraint != null && constraint.getApplyClientSide().booleanValue()) {
 477  0
                     String dependsClass = "dependsOn-" + constraint.getPropertyName();
 478  0
                     String addClass = "jq('[name=\""+ field.getBindingInfo().getBindingPath() + "\"]').addClass('" + dependsClass + "');" +
 479  
                         "jq('[name=\""+ constraint.getPropertyName() + "\"]').addClass('" + "dependsOn-" + field.getBindingInfo().getBindingPath() + "');";
 480  0
                         addScriptToPage(view, field, addClass + getPrerequisiteStatement(field, view, constraint, booleanStatement)
 481  
                                         + getPostrequisiteStatement(field, constraint, booleanStatement));
 482  
                 //special requiredness indicator handling
 483  0
                 String showIndicatorScript = "setupShowReqIndicatorCheck('"+ field.getBindingInfo().getBindingPath() +"', '" + constraint.getPropertyName() + "', " + "function(){\nreturn (coerceValue('" + field.getBindingInfo().getBindingPath() + "') && " + booleanStatement + ");});\n";
 484  0
                 addScriptToPage(view, field, showIndicatorScript);
 485  
                 }
 486  0
         }
 487  
 
 488  
         /**
 489  
          * This method creates the script necessary for executing a prerequisite
 490  
          * rule in which this field occurs after the field specified in the
 491  
          * prerequisite rule - since it requires a specific set of UI logic. Builds
 492  
          * an if statement containing an addMethod jquery validator call. Adds a
 493  
          * "dependsOn" css class to this field for the field specified.
 494  
          * 
 495  
          * @param constraint
 496  
          *            prerequisiteConstraint
 497  
          * @param booleanStatement
 498  
          *            the booleanstatement in js - should return true when the
 499  
          *            validation rule should be applied
 500  
          * @return
 501  
          */
 502  
         private static String getPrerequisiteStatement(InputField field, View view, PrerequisiteConstraint constraint, String booleanStatement) {
 503  0
                 methodKey++;
 504  0
                 String message = "";
 505  0
                 if(StringUtils.isEmpty(constraint.getLabelKey())){
 506  0
                         message = configService.getPropertyValueAsString(UifConstants.Messages.VALIDATION_MSG_KEY_PREFIX + "prerequisite");
 507  
                 }
 508  
                 else{
 509  0
                         message = generateMessageFromLabelKey(constraint.getValidationMessageParams(), constraint.getLabelKey());
 510  
                 }
 511  0
                 if(StringUtils.isEmpty(message)){
 512  0
                         message = "prerequisite - No message";
 513  
                 }
 514  
                 else{
 515  0
                         InputField requiredField = (InputField) view.getViewIndex().getDataFieldByPath(constraint.getPropertyName());
 516  0
                         if(requiredField != null && StringUtils.isNotEmpty(requiredField.getLabel())){
 517  0
                                 message = MessageFormat.format(message, requiredField.getLabel());
 518  
                         }
 519  
                         else{
 520  0
                                 message = MessageFormat.format(message, configService.getPropertyValueAsString(GENERIC_FIELD_MSG_KEY));
 521  
                         }
 522  
                 }
 523  
                 
 524  
                 // field occurs before case
 525  0
                 String methodName = "prConstraint-" + field.getBindingInfo().getBindingPath()+ methodKey;
 526  0
                 String addClass = "jq('[name=\""+ field.getBindingInfo().getBindingPath() + "\"]').addClass('" + methodName + "');\n";
 527  0
                 String method = "\njQuery.validator.addMethod(\""+ methodName +"\", function(value, element) {\n" +
 528  
                         " if(" + booleanStatement + "){ return (this.optional(element) || (coerceValue('" + constraint.getPropertyName() + "')));}else{return true;} " +
 529  
                         "}, \"" + message + "\");";
 530  
                 
 531  0
                 String ifStatement = "if(occursBefore('" + constraint.getPropertyName() + "','" + field.getBindingInfo().getBindingPath() +
 532  
                 "')){" + addClass + method + "}";
 533  0
                 return ifStatement;
 534  
         }
 535  
 
 536  
         /**
 537  
          * This method creates the script necessary for executing a prerequisite
 538  
          * rule in which this field occurs before the field specified in the
 539  
          * prerequisite rule - since it requires a specific set of UI logic. Builds
 540  
          * an if statement containing an addMethod jquery validator call.
 541  
          * 
 542  
          * @param constraint
 543  
          *            prerequisiteConstraint
 544  
          * @param booleanStatement
 545  
          *            the booleanstatement in js - should return true when the
 546  
          *            validation rule should be applied
 547  
          * @return
 548  
          */
 549  
         private static String getPostrequisiteStatement(InputField field, PrerequisiteConstraint constraint, String booleanStatement) {
 550  
                 // field occurs after case
 551  0
                 String message = "";
 552  0
                 if(StringUtils.isEmpty(constraint.getLabelKey())){
 553  0
                         message = configService.getPropertyValueAsString(UifConstants.Messages.VALIDATION_MSG_KEY_PREFIX + "postrequisite");
 554  
                 }
 555  
                 else{
 556  0
                         message = generateMessageFromLabelKey(constraint.getValidationMessageParams(), constraint.getLabelKey());
 557  
                 }
 558  
                 
 559  0
                 if(StringUtils.isEmpty(constraint.getLabelKey())){
 560  0
                         if(StringUtils.isNotEmpty(field.getLabel())){
 561  0
                                 message = MessageFormat.format(message, field.getLabel());
 562  
                         }
 563  
                         else{
 564  0
                                 message = MessageFormat.format(message, configService.getPropertyValueAsString(GENERIC_FIELD_MSG_KEY));
 565  
                         }
 566  
                         
 567  
                 }
 568  
                 
 569  0
                 String function = "function(element){\n" +
 570  
                         "return (coerceValue('"+ field.getBindingInfo().getBindingPath() + "') && " + booleanStatement + ");}";
 571  0
                 String postStatement = "\nelse if(occursBefore('" + field.getBindingInfo().getBindingPath() + "','" + constraint.getPropertyName() +
 572  
                         "')){\njq('[name=\""+ constraint.getPropertyName() +
 573  
                         "\"]').rules(\"add\", { required: \n" + function 
 574  
                         + ", \nmessages: {\nrequired: \""+ message +"\"}});}\n";
 575  
                 
 576  0
                 return postStatement;
 577  
 
 578  
         }
 579  
 
 580  
         /**
 581  
          * This method processes the MustOccurConstraint. The constraint is only
 582  
          * applied when the booleanStatement evaluates to true during validation.
 583  
          * This method creates the addMethod and add rule calls for the jquery
 584  
          * validation plugin necessary for applying this constraint to this field.
 585  
          * 
 586  
          * @param view
 587  
          * @param mc
 588  
          * @param booleanStatement
 589  
          *            the booleanstatement in js - should return true when the
 590  
          *            validation rule should be applied
 591  
          */
 592  
         public static void processMustOccurConstraint(InputField field, View view, MustOccurConstraint mc, String booleanStatement) {
 593  0
                 methodKey++;
 594  0
                 mustOccursPathNames = new ArrayList<List<String>>();
 595  
                 // TODO make this show the fields its requiring
 596  0
                 String methodName = "moConstraint-" + field.getBindingInfo().getBindingPath() + methodKey;
 597  0
                 String method = "\njQuery.validator.addMethod(\""+ methodName +"\", function(value, element) {\n" +
 598  
                 " if(" + booleanStatement + "){return (this.optional(element) || ("+ getMustOccurStatement(field, mc) + "));}else{return true;}" +
 599  
                 "}, \"" + getMustOccursMessage(view, mc) +"\");";
 600  0
                 String rule = method + "jq('[name=\""+ field.getBindingInfo().getBindingPath() + "\"]').rules(\"add\", {\n\"" + methodName + "\": function(element){return (" + booleanStatement + ");}\n});";
 601  0
                 addScriptToPage(view, field, rule);
 602  0
         }
 603  
 
 604  
         /**
 605  
          * This method takes in a MustOccurConstraint and returns the statement used
 606  
          * in determining if the must occurs constraint has been satisfied when this
 607  
          * field is validated. Note the use of the mustOccurCheck method. Nested
 608  
          * mustOccurConstraints are ored against the result of the mustOccurCheck by
 609  
          * calling this method recursively.
 610  
          * 
 611  
          * @param constraint
 612  
          * @return
 613  
          */
 614  
         @SuppressWarnings("boxing")
 615  
         private static String getMustOccurStatement(InputField field, MustOccurConstraint constraint) {
 616  0
                 String statement = "";
 617  0
                 List<String> attributePaths = new ArrayList<String>();
 618  0
                 if (constraint != null && constraint.getApplyClientSide()) {
 619  0
                         String array = "[";
 620  0
                         if (constraint.getPrerequisiteConstraints() != null) {
 621  0
                                 for (int i = 0; i < constraint.getPrerequisiteConstraints().size(); i++) {
 622  0
                                         field.getControl().addStyleClass("dependsOn-"
 623  
                                                         + constraint.getPrerequisiteConstraints().get(i).getPropertyName());
 624  0
                                         array = array + "'" + constraint.getPrerequisiteConstraints().get(i).getPropertyName() + "'";
 625  0
                                         attributePaths.add(constraint.getPrerequisiteConstraints().get(i).getPropertyName());
 626  0
                                         if (i + 1 != constraint.getPrerequisiteConstraints().size()) {
 627  0
                                                 array = array + ",";
 628  
                                         }
 629  
 
 630  
                                 }
 631  
                         }
 632  0
                         array = array + "]";
 633  0
                         statement = "mustOccurTotal(" + array + ", " + constraint.getMin() + ", " + constraint.getMax() + ")";
 634  
                         //add min to string list
 635  0
                         if(constraint.getMin()!=null){
 636  0
                                 attributePaths.add(constraint.getMin().toString());
 637  
                         }
 638  
                         else{
 639  0
                                 attributePaths.add(null);
 640  
                         }
 641  
                         //add max to string list
 642  0
                         if(constraint.getMax()!=null){
 643  0
                                 attributePaths.add(constraint.getMax().toString());
 644  
                         }
 645  
                         else{
 646  0
                                 attributePaths.add(null);
 647  
                         }
 648  
                         
 649  0
                         mustOccursPathNames.add(attributePaths);
 650  0
                         if(StringUtils.isEmpty(statement)){
 651  0
                                 statement = "0";
 652  
                         }
 653  0
                         if (constraint.getMustOccurConstraints() != null) {
 654  0
                                 for (MustOccurConstraint mc : constraint.getMustOccurConstraints()) {
 655  0
                                         statement = "mustOccurCheck(" + statement + " + " + getMustOccurStatement(field, mc) +
 656  
                                                 ", " + constraint.getMin() + ", " + constraint.getMax() + ")";
 657  
                                 }
 658  
                         }
 659  
                         else{
 660  0
                                 statement = "mustOccurCheck(" + statement +
 661  
                                 ", " + constraint.getMin() + ", " + constraint.getMax() + ")";
 662  
                         }
 663  
                 }
 664  0
                 return statement;
 665  
         }
 666  
 
 667  
         
 668  
         /**
 669  
          * Generates a message for the must occur constraint (if no label key is specified).  
 670  
          * This message is most accurate when must occurs is a single
 671  
          * or double level constraint.  Beyond that, the message will still be accurate but may be confusing for
 672  
          * the user - this auto-generated message however will work in MOST use cases.
 673  
          * 
 674  
          * @param view
 675  
          * @return
 676  
          */
 677  
         private static String getMustOccursMessage(View view, MustOccurConstraint constraint){
 678  0
                 String message = "";
 679  0
                 if(StringUtils.isNotEmpty(constraint.getLabelKey())){
 680  0
                         message = generateMessageFromLabelKey(constraint.getValidationMessageParams(), constraint.getLabelKey());
 681  
                 }
 682  
                 else{
 683  0
                         String and = configService.getPropertyValueAsString(AND_MSG_KEY);
 684  0
                         String or = configService.getPropertyValueAsString(OR_MSG_KEY);
 685  0
                         String all = configService.getPropertyValueAsString(ALL_MSG_KEY);
 686  0
                         String atMost = configService.getPropertyValueAsString(ATMOST_MSG_KEY);
 687  0
                         String genericLabel = configService.getPropertyValueAsString(GENERIC_FIELD_MSG_KEY);
 688  0
                         String mustOccursMsg = configService.getPropertyValueAsString(
 689  
                                 UifConstants.Messages.VALIDATION_MSG_KEY_PREFIX + MUSTOCCURS_MSG_KEY);
 690  
                         //String postfix = configService.getPropertyValueAsString(VALIDATION_MSG_KEY_PREFIX + MUSTOCCURS_POST_MSG_KEY);
 691  0
                         String statement="";
 692  0
                         for(int i=0; i< mustOccursPathNames.size(); i++){
 693  0
                                 String andedString = "";
 694  
                                 
 695  0
                                 List<String> paths = mustOccursPathNames.get(i);
 696  0
                                 if(!paths.isEmpty()){
 697  
                                         //note that the last 2 strings are min and max and rest are attribute paths
 698  0
                                         String min = paths.get(paths.size()-2);
 699  0
                                         String max = paths.get(paths.size()-1);
 700  0
                                         for(int j=0; j<paths.size()-2;j++){
 701  0
                                                 InputField field = (InputField) view.getViewIndex().getDataFieldByPath(paths.get(j).trim());
 702  0
                                                 String label = genericLabel;
 703  0
                                                 if(field != null && StringUtils.isNotEmpty(field.getLabel())){
 704  0
                                                         label = field.getLabel();
 705  
                                                 }
 706  0
                                                 if(min.equals(max)){
 707  0
                                                         if(j==0){
 708  0
                                                                 andedString = label;
 709  
                                                         }
 710  0
                                                         else if(j==paths.size()-3){
 711  0
                                                                 andedString = andedString + " " + and + " " + label;
 712  
                                                         }
 713  
                                                         else{
 714  0
                                                                 andedString = andedString + ", " + label;
 715  
                                                         }
 716  
                                                 }
 717  
                                                 else{
 718  0
                                                         andedString = andedString + "<li>" + label + "</li>";
 719  
                                                 }
 720  
                                         }
 721  0
                                         if(min.equals(max)){
 722  0
                                                 andedString = "<li>" + andedString + "</li>";
 723  
                                         }
 724  0
                                         andedString="<ul>" + andedString + "</ul>";
 725  
                                 
 726  0
                                         if(StringUtils.isNotEmpty(min) && StringUtils.isNotEmpty(max) && !min.equals(max)){
 727  0
                                                 andedString = MessageFormat.format(mustOccursMsg, min + "-" + max) + "<br/>" +andedString;
 728  
                                         }
 729  0
                                         else if(StringUtils.isNotEmpty(min) && StringUtils.isNotEmpty(max) && min.equals(max) && i==0){
 730  0
                                                 andedString = MessageFormat.format(mustOccursMsg, all) + "<br/>" +andedString;
 731  
                                         }
 732  0
                                         else if(StringUtils.isNotEmpty(min) && StringUtils.isNotEmpty(max) && min.equals(max) && i!=0){
 733  
                                                 //leave andedString as is
 734  
                                         }
 735  0
                                         else if(StringUtils.isNotEmpty(min)){
 736  0
                                                 andedString = MessageFormat.format(mustOccursMsg, min) + "<br/>" +andedString;
 737  
                                         }
 738  0
                                         else if(StringUtils.isNotEmpty(max)){
 739  0
                                                 andedString = MessageFormat.format(mustOccursMsg, atMost + " " + max) + "<br/>" +andedString;
 740  
                                         }
 741  
                                 }
 742  0
                                 if(StringUtils.isNotEmpty(andedString)){
 743  0
                                         if(i==0){
 744  0
                                                 statement = andedString;
 745  
                                         }
 746  
                                         else{
 747  0
                                                 statement = statement + or.toUpperCase() + andedString;
 748  
                                         }
 749  
                                 }
 750  
                         }
 751  0
                         if(StringUtils.isNotEmpty(statement)){
 752  0
                                 message = statement;
 753  
                         }
 754  
                 }
 755  
                 
 756  0
                 return message;
 757  
         }
 758  
 
 759  
         /**
 760  
          * This method processes all the constraints on the InputField passed in and adds all the necessary
 761  
          * jQuery and js required (validator's rules, methods, and messages) to the View's onDocumentReady call.
 762  
          * The result is js that will validate all the constraints contained on an InputField during user interaction
 763  
          * with the field using the jQuery validation plugin and custom code.
 764  
          * 
 765  
          * @param field
 766  
          */
 767  
         @SuppressWarnings("boxing")
 768  
         public static void processAndApplyConstraints(InputField field, View view) {
 769  0
                 methodKey = 0;
 770  0
                 if ((field.getRequired() != null) && (field.getRequired().booleanValue())) {
 771  0
                         field.getControl().addStyleClass("required");
 772  
                 }
 773  
 
 774  0
                 if (field.getExclusiveMin() != null) {
 775  0
                         if (field.getControl() instanceof TextControl && ((TextControl) field.getControl()).getDatePicker() != null) {
 776  0
                                 ((TextControl) field.getControl()).getDatePicker().getComponentOptions().put("minDate", field.getExclusiveMin());
 777  
                         }
 778  
                         else{
 779  0
                                 String rule = "jq('[name=\""+ field.getBindingInfo().getBindingPath() + "\"]').rules(\"add\", {\n minExclusive: ["+ field.getExclusiveMin() + "]});";
 780  0
                                 addScriptToPage(view, field, rule);
 781  
                         }
 782  
                 }
 783  
 
 784  0
                 if (field.getInclusiveMax() != null) {
 785  0
                         if (field.getControl() instanceof TextControl && ((TextControl) field.getControl()).getDatePicker() != null) {
 786  0
                                 ((TextControl) field.getControl()).getDatePicker().getComponentOptions().put("maxDate", field.getInclusiveMax());
 787  
                         }
 788  
                         else{
 789  0
                                 String rule = "jq('[name=\""+ field.getBindingInfo().getBindingPath() + "\"]').rules(\"add\", {\n maxInclusive: ["+ field.getInclusiveMax() + "]});";
 790  0
                                 addScriptToPage(view, field, rule);
 791  
                         }
 792  
                 }
 793  
 
 794  0
                 if (field.getValidCharactersConstraint() != null && field.getValidCharactersConstraint().getApplyClientSide()) {
 795  0
                         if(StringUtils.isNotEmpty(field.getValidCharactersConstraint().getValue())) {
 796  
                                 // set regex value takes precedence
 797  0
                                 addScriptToPage(view, field, ClientValidationUtils.getRegexMethod(field, field.getValidCharactersConstraint()));
 798  0
                                 field.getControl().addStyleClass("validChar-" + field.getBindingInfo().getBindingPath()+ methodKey);
 799  0
                                 methodKey++;
 800  
                         }
 801  
                         else {
 802  
                                 //blindly assume that if there is no regex value defined that there must be a method by this name
 803  0
                                 if(StringUtils.isNotEmpty(field.getValidCharactersConstraint().getLabelKey())){
 804  0
                                         field.getControl().addStyleClass(field.getValidCharactersConstraint().getLabelKey());
 805  
                                 }
 806  
                         }
 807  
                 }
 808  
 
 809  0
                 if (field.getCaseConstraint() != null && field.getCaseConstraint().getApplyClientSide()) {
 810  0
                         processCaseConstraint(field, view, field.getCaseConstraint(), null);
 811  
                 }
 812  
 
 813  0
                 if (field.getDependencyConstraints() != null) {
 814  0
                         for (PrerequisiteConstraint prc : field.getDependencyConstraints()) {
 815  0
                                 processPrerequisiteConstraint(field, prc, view);
 816  
                         }
 817  
                 }
 818  
 
 819  0
                 if (field.getMustOccurConstraints() != null) {
 820  0
                         for (MustOccurConstraint mc : field.getMustOccurConstraints()) {
 821  0
                                 processMustOccurConstraint(field, view, mc, "true");
 822  
                         }
 823  
                 }
 824  
                 
 825  0
         }
 826  
 
 827  
 }