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