Coverage Report - org.kuali.student.common.validator.old.Validator
 
Classes in this File Line Coverage Branch Coverage Complexity
Validator
54%
238/439
36%
140/386
6.722
Validator$1
N/A
N/A
6.722
Validator$BaseConstraintBean
100%
18/18
N/A
6.722
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 package org.kuali.student.common.validator.old;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Collection;
 20  
 import java.util.Date;
 21  
 import java.util.HashMap;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.Stack;
 26  
 
 27  
 import org.kuali.student.common.util.MessageUtils;
 28  
 import org.kuali.student.core.dictionary.old.dto.CaseConstraint;
 29  
 import org.kuali.student.core.dictionary.old.dto.ConstraintDescriptor;
 30  
 import org.kuali.student.core.dictionary.old.dto.ConstraintSelector;
 31  
 import org.kuali.student.core.dictionary.old.dto.Field;
 32  
 import org.kuali.student.core.dictionary.old.dto.LookupConstraint;
 33  
 import org.kuali.student.core.dictionary.old.dto.ObjectStructure;
 34  
 import org.kuali.student.core.dictionary.old.dto.OccursConstraint;
 35  
 import org.kuali.student.core.dictionary.old.dto.RequireConstraint;
 36  
 import org.kuali.student.core.dictionary.old.dto.State;
 37  
 import org.kuali.student.core.dictionary.old.dto.Type;
 38  
 import org.kuali.student.core.dictionary.old.dto.TypeStateCaseConstraint;
 39  
 import org.kuali.student.core.dictionary.old.dto.ValidCharsConstraint;
 40  
 import org.kuali.student.core.dictionary.old.dto.WhenConstraint;
 41  
 import org.kuali.student.core.messages.dto.Message;
 42  
 import org.kuali.student.core.messages.service.MessageService;
 43  
 import org.kuali.student.core.validation.dto.ValidationResultInfo;
 44  
 
 45  41
 public class Validator {
 46  
 
 47  
         //TODO: Change this to 'default' when the change is made in xml
 48  
         private static final String DEFAULT_STATE = "*";
 49  
 
 50  1
         private static final String UNBOUNDED_CHECK = null;
 51  
 
 52  9
         private MessageService messageService = null;
 53  
 
 54  9
         private String messageLocaleKey = "en";
 55  
 
 56  9
         private String messageGroupKey = "validation";
 57  
 
 58  9
         private DateParser dateParser = new ServerDateParser();
 59  
 
 60  9
         private boolean serverSide = true;
 61  
 
 62  
         public MessageService getMessageService() {
 63  0
                 return messageService;
 64  
         }
 65  
 
 66  
         public void setMessageService(MessageService messageService) {
 67  9
                 this.messageService = messageService;
 68  9
         }
 69  
 
 70  
         public String getMessageLocaleKey() {
 71  0
                 return messageLocaleKey;
 72  
         }
 73  
 
 74  
         public void setMessageLocaleKey(String messageLocaleKey) {
 75  0
                 this.messageLocaleKey = messageLocaleKey;
 76  0
         }
 77  
 
 78  
         public String getMessageGroupKey() {
 79  0
                 return messageGroupKey;
 80  
         }
 81  
 
 82  
         public void setMessageGroupKey(String messageGroupKey) {
 83  0
                 this.messageGroupKey = messageGroupKey;
 84  0
         }
 85  
 
 86  
         public void setDateParser(DateParser dateParser) {
 87  9
                 this.dateParser = dateParser;
 88  9
         }
 89  
 
 90  
         /**
 91  
          * @return the serverSide
 92  
          */
 93  
         public boolean isServerSide() {
 94  0
                 return serverSide;
 95  
         }
 96  
 
 97  
         /**
 98  
          * @param serverSide
 99  
          *            the serverSide to set
 100  
          */
 101  
         public void setServerSide(boolean serverSide) {
 102  0
                 this.serverSide = serverSide;
 103  0
         }
 104  
 
 105  
         /**
 106  
          * @return the dateParser
 107  
          */
 108  
         public DateParser getDateParser() {
 109  0
                 return dateParser;
 110  
         }
 111  
 
 112  
 
 113  
 
 114  
 
 115  
         /**
 116  
          * Validate Object and all its nested child objects for given type and state
 117  
          *
 118  
          * @param data
 119  
          * @param objStructure
 120  
          * @return
 121  
          */
 122  
         public List<ValidationResultInfo> validateTypeStateObject(Object data,
 123  
                         ObjectStructure objStructure) {
 124  
 
 125  9
                 Stack<String> elementStack = new Stack<String>();
 126  9
                 return validateTypeStateObject(data, objStructure, elementStack);
 127  
         }
 128  
 
 129  
         private List<ValidationResultInfo> validateTypeStateObject(Object data,
 130  
                         ObjectStructure objStructure, Stack<String> elementStack) {
 131  
 
 132  10
                 List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 133  
 
 134  10
                 ConstraintDataProvider dataProvider = new BeanConstraintDataProvider();
 135  10
                 dataProvider.initialize(data);
 136  
 
 137  10
                 boolean isTypeStateObject = (dataProvider.hasField("type") && dataProvider
 138  
                                 .hasField("state"));
 139  
 
 140  
                 // Push object structure to the top of the stack
 141  10
                 StringBuilder objXPathElement = new StringBuilder(dataProvider
 142  
                                 .getPath());
 143  10
                 if (null != dataProvider.getObjectId()) {
 144  10
                         objXPathElement.append("[id='" + dataProvider.getObjectId() + "']");
 145  
                 } else {
 146  0
                         objXPathElement.append("[id='null']");
 147  
                 }
 148  10
                 elementStack.push(objXPathElement.toString());
 149  
 
 150  
                 /*
 151  
                  * Do nothing if the object to be validated is not type/state or if the
 152  
                  * objectstructure with constraints is not provided
 153  
                  */
 154  10
                 if (!isTypeStateObject || null == objStructure) {
 155  0
                         return results;
 156  
                 }
 157  
 
 158  
                 // Validate with the matching Type/State
 159  10
                 List<Type> types = objStructure.getType();
 160  10
                 for (Type t : types) {
 161  10
                         if (t.getKey().equalsIgnoreCase(
 162  
                                         (String) dataProvider.getValue("type"))) {
 163  10
                                 for (State s : t.getState()) {
 164  10
                                         if (s.getKey().equalsIgnoreCase(
 165  
                                                         (String) dataProvider.getValue("state"))
 166  
                                                         || s.getKey().equalsIgnoreCase(DEFAULT_STATE)) {
 167  10
                                                 for (Field f : s.getField()) {
 168  35
                                                         List<ValidationResultInfo> l = validateField(f, t,
 169  
                                                                         s, objStructure, dataProvider, elementStack);
 170  35
                                                         results.addAll(l);
 171  35
                                                 }
 172  10
                                                 break;
 173  
                                         }
 174  
                                 }
 175  10
                                 break;
 176  
                         }
 177  
                 }
 178  10
                 elementStack.pop();
 179  
 
 180  
                 /* All Field validations are returned right now */
 181  
                 // List<ValidationResultInfo> resultsBuffer = new
 182  
                 // ArrayList<ValidationResultInfo>();
 183  
                 // for (ValidationResultContainer vc : results) {
 184  
                 // if (skipFields.contains(vc.getElement()) == false) {
 185  
                 // resultsBuffer.add(vc);
 186  
                 // }
 187  
                 // }
 188  
                 // results = resultsBuffer;
 189  10
                 return results;
 190  
         }
 191  
 
 192  
         public List<ValidationResultInfo> validateField(Field field, Type type,
 193  
                         State state, ObjectStructure objStruct,
 194  
                         ConstraintDataProvider dataProvider, Stack<String> elementStack) {
 195  
 
 196  35
                 Object value = dataProvider.getValue(field.getKey());
 197  35
                 List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 198  
 
 199  35
                 ConstraintDescriptor cd = field.getConstraintDescriptor();
 200  
 
 201  
                 // Handle null values in field
 202  35
                 if (value == null || "".equals(value.toString().trim())) {
 203  19
                         if (isNullable(field) == false) {
 204  9
                                 ValidationResultInfo valInfo = new ValidationResultInfo(
 205  
                                                 getElementXpath(elementStack) + field.getKey());
 206  9
                                 valInfo.setError(getMessage("validation.required"));
 207  9
                                 results.add(valInfo);
 208  
                         }
 209  19
                         return results;
 210  
                 }
 211  
 
 212  
                 /*
 213  
                  * For complex object structures only the following constraints apply
 214  
                  * 1. TypeStateCase
 215  
                  * 2. MinOccurs
 216  
                  * 3. MaxOccurs
 217  
                  */
 218  16
                 if ("complex"
 219  
                                 .equalsIgnoreCase(field.getFieldDescriptor().getDataType())) {
 220  1
                         ObjectStructure nestedObjStruct = null;
 221  
 
 222  1
                         if(null != field.getFieldDescriptor().getObjectStructure()) {
 223  1
                                 nestedObjStruct = field.getFieldDescriptor()
 224  
                                 .getObjectStructure();
 225  
                         }
 226  0
                         else if (hasText(field.getFieldDescriptor().getObjectStructureRef())) {
 227  
                                 //TODO: Setup a mechanism to retrive referenced object structures
 228  
 //                                nestedObjStruct = setupFactory.getObjectStructure(field
 229  
 //                                                .getFieldDescriptor().getObjectStructureRef());
 230  
                         }
 231  
 
 232  1
                         BaseConstraintBean bcb = new BaseConstraintBean();
 233  1
                         if(null != cd) {
 234  0
                                 for(ConstraintSelector constraint: cd.getConstraint()) {
 235  0
                                         computeBaseConstraints(constraint, bcb, field);
 236  
                                 }
 237  
                         }
 238  
 
 239  1
                         elementStack.push(field.getKey());
 240  
 
 241  1
                         if (value instanceof Collection<?>) {
 242  
 
 243  1
                                 String xPath = getElementXpath(elementStack) + "/";
 244  
 
 245  1
                                 for (Object o : (Collection<?>) value) {
 246  1
                                         processNestedObjectStructure(results, o, nestedObjStruct,
 247  
                                                         field, elementStack);
 248  
                                 }
 249  1
                                 if (bcb.minOccurs > ((Collection<?>) value).size()) {
 250  0
                                         ValidationResultInfo valRes = new ValidationResultInfo(
 251  
                                                         xPath);
 252  0
                                         valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), bcb.toMap()));
 253  0
                                         results.add(valRes);
 254  
                                 }
 255  
 
 256  1
                                 Integer maxOccurs = tryParse(bcb.maxOccurs);
 257  1
                                 if (maxOccurs != null
 258  
                                                 && maxOccurs < ((Collection<?>) value).size()) {
 259  0
                                         ValidationResultInfo valRes = new ValidationResultInfo(
 260  
                                                         xPath);
 261  0
                                         valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), bcb.toMap()));
 262  0
                                         results.add(valRes);
 263  
                                 }
 264  1
                         } else {
 265  0
                                 if(null != value) {
 266  0
                                         processNestedObjectStructure(results, value, nestedObjStruct,
 267  
                                                         field, elementStack);
 268  
                                 } else {
 269  0
                                         if (bcb.minOccurs != null && bcb.minOccurs > 0) {
 270  0
                                                 ValidationResultInfo val = new ValidationResultInfo(
 271  
                                                                 getElementXpath(elementStack) + "[value='null']/");
 272  0
                                                 val.setError(getMessage("validation.required"));
 273  0
                                                 results.add(val);
 274  
                                         }
 275  
                                 }
 276  
                         }
 277  
 
 278  1
                         elementStack.pop();
 279  
 
 280  1
                 } else { // If non complex data type
 281  15
                         if (null != cd) {
 282  15
                                 List<ConstraintSelector> constraints = cd.getConstraint();
 283  
 
 284  15
                                 if (value instanceof Collection<?>) {
 285  0
                                         BaseConstraintBean bcb = new BaseConstraintBean();
 286  0
                                         for (Object o : (Collection<?>) value) {
 287  0
                                                 for (ConstraintSelector constraint : constraints) {
 288  0
                                                         processConstraint(results, constraint, field, type,
 289  
                                                                         state, objStruct, o, dataProvider, bcb, elementStack);
 290  
                                                 }
 291  0
                                                 processBaseConstraints(results, bcb, field, o, elementStack);
 292  0
                                                 if(!bcb.initialized) {
 293  0
                                                         bcb.initialized = true;
 294  
                                                 }
 295  
                                         }
 296  
 
 297  0
                                         String xPath = getElementXpath(elementStack) + field.getKey() + "/";
 298  0
                                         if (bcb.minOccurs > ((Collection<?>) value).size()) {
 299  0
                                                 ValidationResultInfo valRes = new ValidationResultInfo(
 300  
                                                                 xPath);
 301  0
                                                 valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), bcb.toMap()));
 302  0
                                                 results.add(valRes);
 303  
                                         }
 304  
 
 305  0
                                         Integer maxOccurs = tryParse(bcb.maxOccurs);
 306  0
                                         if (maxOccurs != null
 307  
                                                         && maxOccurs < ((Collection<?>) value).size()) {
 308  0
                                                 ValidationResultInfo valRes = new ValidationResultInfo(
 309  
                                                                 xPath);
 310  0
                                                 valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), bcb.toMap()));
 311  0
                                                 results.add(valRes);
 312  
                                         }
 313  0
                                 } else {
 314  15
                                         BaseConstraintBean bcb = new BaseConstraintBean();
 315  15
                                         for (ConstraintSelector constraint : constraints) {
 316  24
                                                 processConstraint(results, constraint, field, type,
 317  
                                                                 state, objStruct, value, dataProvider, bcb, elementStack);
 318  
                                         }
 319  15
                                         processBaseConstraints(results, bcb, field, value, elementStack);
 320  
                                 }
 321  
                         }
 322  
                 }
 323  16
                 return results;
 324  
         }
 325  
 
 326  
         private boolean isNullable(Field field) {
 327  19
                 ConstraintDescriptor cd = field.getConstraintDescriptor();
 328  19
                 if (null != cd) {
 329  19
                         List<ConstraintSelector> constraintList = cd.getConstraint();
 330  19
                         for (ConstraintSelector cs : constraintList) {
 331  19
                                 if (cs.getMinOccurs() != null && cs.getMinOccurs() > 0) {
 332  9
                                         return false;
 333  
                                 }
 334  
                         }
 335  
                 }
 336  10
                 return true;
 337  
         }
 338  
 
 339  
         private Integer tryParse(String s) {
 340  31
                 Integer result = null;
 341  31
                 if (s != null) {
 342  
                         try {
 343  18
                                 result = Integer.valueOf(s);
 344  0
                         } catch (NumberFormatException e) {
 345  
                                 // do nothing
 346  18
                         }
 347  
                 }
 348  31
                 return result;
 349  
         }
 350  
 
 351  
         private void processNestedObjectStructure(
 352  
                         List<ValidationResultInfo> results, Object value,
 353  
                         ObjectStructure nestedObjStruct, Field field, Stack<String> elementStack) {
 354  
 
 355  1
                 results.addAll(validateTypeStateObject(value, nestedObjStruct, elementStack));
 356  
 
 357  1
                 ConstraintDescriptor cd = field.getConstraintDescriptor();
 358  1
                 if (null != cd) {
 359  0
                         ConstraintSelector cs = cd.getConstraint().get(0);
 360  0
                         TypeStateCaseConstraint tscs = cs.getTypeStateCaseConstraint();
 361  0
                         if (null != tscs) {
 362  
                                 // ValidationResultContainer vc = new
 363  
                                 // ValidationResultContainer();
 364  
                                 // processTypeStateCaseConstraint(vc);
 365  
                                 // results.add(vc);k
 366  
                         }
 367  
                 }
 368  1
         }
 369  
 
 370  
         private void computeBaseConstraints(ConstraintSelector constraint,
 371  
                         BaseConstraintBean bcb, Field field) {
 372  24
                 if (null != constraint.getMinLength()) {
 373  10
                         bcb.minLength = (bcb.minLength > constraint.getMinLength()) ? bcb.minLength
 374  
                                         : constraint.getMinLength();
 375  
                 }
 376  
 
 377  24
                 if (null != constraint.getMinOccurs()) {
 378  13
                         bcb.minOccurs = (bcb.minOccurs > constraint.getMinOccurs()) ? bcb.minOccurs
 379  
                                         : constraint.getMinOccurs();
 380  
                 }
 381  
 
 382  24
                 if (null != constraint.getMinValue()) {
 383  4
                         bcb.minValue = (null == bcb.minValue || ValidatorUtils
 384  
                                         .compareValues(bcb.minValue, constraint.getMinValue(),
 385  
                                                         field.getFieldDescriptor().getDataType(),
 386  
                                                         "GREATER_THAN", dateParser)) ? constraint
 387  
                                         .getMinValue() : bcb.minValue;
 388  
                 }
 389  
 
 390  24
                 if (null != constraint.getMaxValue()) {
 391  1
                         bcb.maxValue = (null == bcb.maxValue || ValidatorUtils
 392  
                                         .compareValues(bcb.maxValue, constraint.getMaxValue(),
 393  
                                                         field.getFieldDescriptor().getDataType(),
 394  
                                                         "LESS_THAN", dateParser)) ? constraint
 395  
                                         .getMaxValue() : bcb.maxValue;
 396  
                 }
 397  
 
 398  24
                 if (hasText(constraint.getMaxLength())) {
 399  9
                         Integer maxLength = tryParse(bcb.maxLength);
 400  9
                         Integer constraintMaxLength = tryParse(constraint.getMaxLength());
 401  9
                         if (maxLength == null) {
 402  9
                                 bcb.maxLength = constraint.getMaxLength();
 403  0
                         } else if (constraintMaxLength != null) {
 404  0
                                 if (constraintMaxLength > maxLength) {
 405  0
                                         bcb.maxLength = constraint.getMaxLength();
 406  
                                 }
 407  
                         }
 408  
                 }
 409  
 
 410  24
                 if (hasText(constraint.getMaxOccurs())) {
 411  1
                         Integer maxOccurs = tryParse(bcb.maxOccurs);
 412  1
                         Integer constraintMaxOccurs = tryParse(constraint.getMaxOccurs());
 413  1
                         if (maxOccurs == null) {
 414  1
                                 bcb.maxOccurs = constraint.getMaxOccurs();
 415  0
                         } else if (constraintMaxOccurs != null) {
 416  0
                                 if (constraintMaxOccurs > maxOccurs) {
 417  0
                                         bcb.maxOccurs = constraint.getMaxOccurs();
 418  
                                 }
 419  
                         }
 420  
                 }
 421  24
         }
 422  
 
 423  
         private void processConstraint(List<ValidationResultInfo> valResults,
 424  
                         ConstraintSelector constraint, Field field, Type type, State state,
 425  
                         ObjectStructure objStructure, Object value,
 426  
                         ConstraintDataProvider dataProvider, BaseConstraintBean bcb,
 427  
                         Stack<String> elementStack) {
 428  
 
 429  
                 // If constraint is only to be processed on server side
 430  24
                 if (hasText(constraint.getClassName()) || constraint.isServerSide()
 431  
                                 && !serverSide) {
 432  0
                         return;
 433  
                 }
 434  
 
 435  24
                 String elementPath = getElementXpath(elementStack) + field.getKey()
 436  
                                 + "[value='" + value.toString() + "']/";
 437  
 
 438  24
                 if(!bcb.initialized) {
 439  24
                         computeBaseConstraints(constraint, bcb, field);
 440  
                 }
 441  
 
 442  
                 // Process Valid Chars
 443  24
                 if (null != constraint.getValidChars()) {
 444  9
                         ValidationResultInfo val = processValidCharConstraint(elementPath,
 445  
                                         constraint.getValidChars(), dataProvider, value);
 446  9
                         if (null != val) {
 447  2
                                 valResults.add(val);
 448  
                         }
 449  
                 }
 450  
 
 451  
                 // Process Require Constraints (only if this field has value)
 452  24
                 if (value != null && !"".equals(value.toString().trim())) {
 453  24
                         if (null != constraint.getRequireConstraint()
 454  
                                         && constraint.getRequireConstraint().size() > 0) {
 455  1
                                 for (RequireConstraint rc : constraint.getRequireConstraint()) {
 456  1
                                         ValidationResultInfo val = processRequireConstraint(
 457  
                                                         elementPath, rc, field, objStructure, dataProvider);
 458  1
                                         if (null != val) {
 459  1
                                                 valResults.add(val);
 460  
                                         }
 461  1
                                 }
 462  
                         }
 463  
                 }
 464  
 
 465  
                 // Process Occurs Constraint
 466  24
                 if (null != constraint.getOccursConstraint()
 467  
                                 && constraint.getOccursConstraint().size() > 0) {
 468  0
                         for (OccursConstraint oc : constraint.getOccursConstraint()) {
 469  0
                                 ValidationResultInfo val = processOccursConstraint(elementPath,
 470  
                                                 oc, field, type, state, objStructure, dataProvider);
 471  0
                                 if (null != val) {
 472  0
                                         valResults.add(val);
 473  
                                 }
 474  0
                         }
 475  
                 }
 476  
 
 477  
                 // Process lookup Constraint
 478  24
                 if (null != constraint.getLookupConstraint()
 479  
                                 && constraint.getLookupConstraint().size() > 0) {
 480  0
                         for (LookupConstraint lc : constraint.getLookupConstraint()) {
 481  0
                                 processLookupConstraint(valResults);
 482  
                         }
 483  
                 }
 484  
 
 485  
                 // Process Case Constraint
 486  24
                 if (null != constraint.getCaseConstraint()
 487  
                                 && constraint.getCaseConstraint().size() > 0) {
 488  0
                         for (CaseConstraint cc : constraint.getCaseConstraint()) {
 489  0
                                 processCaseConstraint(valResults, cc, field, type, state,
 490  
                                                 objStructure, value, dataProvider, bcb, elementStack);
 491  
                         }
 492  
                 }
 493  24
         }
 494  
 
 495  
         private ValidationResultInfo processRequireConstraint(String element,
 496  
                         RequireConstraint constraint, Field field,
 497  
                         ObjectStructure objStructure, ConstraintDataProvider dataProvider) {
 498  
 
 499  1
                 ValidationResultInfo val = null;
 500  
 
 501  1
                 String fieldName = constraint.getField();
 502  1
                 Object fieldValue = dataProvider.getValue(fieldName);
 503  
 
 504  1
                 boolean result = true;
 505  
 
 506  1
                 if (fieldValue instanceof java.lang.String) {
 507  1
                         result = hasText((String) fieldValue);
 508  0
                 } else if (fieldValue instanceof Collection<?>) {
 509  0
                         result = (((Collection<?>) fieldValue).size() > 0);
 510  
                 } else {
 511  0
                         result = (null != fieldValue) ? true : false;
 512  
                 }
 513  
 
 514  1
                 if (!result) {
 515  1
                         Map<String, Object> rMap = new HashMap<String, Object>();
 516  1
                         rMap.put("field1", field.getKey());
 517  1
                         rMap.put("field2", fieldName);
 518  1
                         val = new ValidationResultInfo(element);
 519  1
                         val.setError(MessageUtils.interpolate(
 520  
                                         getMessage("validation.requiresField"), rMap));
 521  
                 }
 522  
 
 523  1
                 return val;
 524  
         }
 525  
 
 526  
         /**
 527  
          * Process caseConstraint tag and sets any of the base constraint items if
 528  
          * any of the when condition matches
 529  
          *
 530  
          * @param bcb
 531  
          * @param caseConstraint
 532  
          * @param field
 533  
          */
 534  
         private void processCaseConstraint(List<ValidationResultInfo> valResults,
 535  
                         CaseConstraint constraint, Field field, Type type, State state,
 536  
                         ObjectStructure objStructure, Object value,
 537  
                         ConstraintDataProvider dataProvider, BaseConstraintBean bcb,
 538  
                         Stack<String> elementStack) {
 539  
 
 540  0
                 String operator = (hasText(constraint.getOperator())) ? constraint
 541  
                                 .getOperator() : "EQUALS";
 542  0
                 Field caseField = (hasText(constraint.getField())) ? ValidatorUtils
 543  
                                 .getField(constraint.getField(), objStructure, type.getKey(),
 544  
                                                 state.getKey()) : null;
 545  
 
 546  
                 // TODO: What happens when the field is not in the dataProvider?
 547  0
                 Object fieldValue = (null != caseField) ? dataProvider
 548  
                                 .getValue(caseField.getKey()) : value;
 549  
 
 550  
                 // Extract value for field Key
 551  0
                 for (WhenConstraint wc : constraint.getWhenConstraint()) {
 552  0
                         String whenValue = wc.getValue();
 553  
 
 554  0
                         if (ValidatorUtils.compareValues(fieldValue, whenValue, caseField
 555  
                                         .getFieldDescriptor().getDataType(), operator, dateParser)) {
 556  0
                                 processConstraint(valResults, wc.getConstraint(), field, type,
 557  
                                                 state, objStructure, value, dataProvider, bcb,
 558  
                                                 elementStack);
 559  
                         }
 560  0
                 }
 561  0
         }
 562  
 
 563  
         private ValidationResultInfo processValidCharConstraint(String element,
 564  
                         ValidCharsConstraint vcConstraint,
 565  
                         ConstraintDataProvider dataProvider, Object value) {
 566  
 
 567  9
                 ValidationResultInfo val = null;
 568  
 
 569  9
                 StringBuilder fieldValue = new StringBuilder();
 570  9
                 String validChars = vcConstraint.getValue();
 571  9
                 String fields = vcConstraint.getFields();
 572  
 
 573  9
                 if (hasText(fields)) {
 574  0
                         String separator = vcConstraint.getSeparator();
 575  0
                         String[] fieldNameList = fields.split(",");
 576  
 
 577  0
                         int sz = fieldNameList.length;
 578  
 
 579  0
                         for (String fieldName : fieldNameList) {
 580  0
                                 Object v = dataProvider.getValue(fieldName);
 581  0
                                 fieldValue.append(ValidatorUtils.getString(v));
 582  
 
 583  0
                                 if (--sz > 0) {
 584  0
                                         fieldValue.append(separator);
 585  
                                 }
 586  
                         }
 587  0
                 } else {
 588  9
                         fieldValue.append(ValidatorUtils.getString(value));
 589  
                 }
 590  
 
 591  9
                 int typIdx = validChars.indexOf(":");
 592  9
                 String processorType = "regex";
 593  9
                 if (-1 == typIdx) {
 594  0
                         validChars = "[" + validChars + "]*";
 595  
                 } else {
 596  9
                         processorType = validChars.substring(0, typIdx);
 597  9
                         validChars = validChars.substring(typIdx + 1);
 598  
                 }
 599  
 
 600  
                 // TODO: Allow different processing based on the label
 601  9
                 if ("regex".equalsIgnoreCase(processorType)) {
 602  
                         // if (!Pattern.matches(validChars, fieldValue.toString())) {
 603  9
                         if (fieldValue == null) {
 604  0
                                 val = new ValidationResultInfo(element);
 605  0
                                 val.setError(getMessage("validation.validCharsFailed"));
 606  9
                         } else if (fieldValue != null
 607  
                                         && !fieldValue.toString().matches(validChars)) {
 608  2
                                 val = new ValidationResultInfo(element);
 609  2
                                 val.setError(getMessage("validation.validCharsFailed"));
 610  
                         }
 611  
                 }
 612  
 
 613  9
                 return val;
 614  
         }
 615  
 
 616  
         /**
 617  
          * Computes if all the filed required in the occurs clause are between the
 618  
          * min and max
 619  
          *
 620  
          * @param valResults
 621  
          * @param constraint
 622  
          * @param field
 623  
          * @param type
 624  
          * @param state
 625  
          * @param objStructure
 626  
          * @param dataProvider
 627  
          * @return
 628  
          */
 629  
         private ValidationResultInfo processOccursConstraint(String element,
 630  
                         OccursConstraint constraint, Field field, Type type, State state,
 631  
                         ObjectStructure objStructure, ConstraintDataProvider dataProvider) {
 632  
 
 633  0
                 boolean result = false;
 634  0
                 int trueCount = 0;
 635  
 
 636  0
                 ValidationResultInfo val = null;
 637  
 
 638  0
                 for (RequireConstraint rc : constraint.getRequire()) {
 639  0
                         trueCount += (processRequireConstraint("", rc, field, objStructure,
 640  
                                         dataProvider) != null) ? 1 : 0;
 641  
                 }
 642  
 
 643  0
                 for (OccursConstraint oc : constraint.getOccurs()) {
 644  0
                         trueCount += (processOccursConstraint("", oc, field, type, state,
 645  
                                         objStructure, dataProvider) != null) ? 1 : 0;
 646  
                 }
 647  
 
 648  0
                 result = (trueCount >= constraint.getMin() && trueCount <= constraint
 649  
                                 .getMax()) ? true : false;
 650  
 
 651  0
                 if (!result) {
 652  0
                         val = new ValidationResultInfo(element);
 653  0
                         val.setError(getMessage("validation.occurs"));
 654  
                 }
 655  
 
 656  0
                 return val;
 657  
         }
 658  
 
 659  
         // TODO: Implement lookup constraint
 660  
         private void processLookupConstraint(List<ValidationResultInfo> valResults) {
 661  0
         }
 662  
 
 663  
         // TODO: Implement TypeStateCase constraint
 664  
         private void processTypeStateCaseConstraint(
 665  
                         List<ValidationResultInfo> valResults) {
 666  0
         }
 667  
 
 668  
         private void processBaseConstraints(List<ValidationResultInfo> valResults,
 669  
                         BaseConstraintBean bcb, Field field, Object value,
 670  
                         Stack<String> elementStack) {
 671  
 
 672  15
                 String dataType = field.getFieldDescriptor().getDataType();
 673  
 
 674  15
                 if (value == null || "".equals(value.toString().trim())) {
 675  0
                         if (bcb.minOccurs != null && bcb.minOccurs > 0) {
 676  0
                                 ValidationResultInfo val = new ValidationResultInfo(
 677  
                                                 getElementXpath(elementStack) + field.getKey()
 678  
                                                                 + "[value='null']/");
 679  0
                                 val.setError(getMessage("validation.required"));
 680  0
                                 valResults.add(val);
 681  0
                                 return;
 682  
                         }
 683  
                 }
 684  
 
 685  15
                 String elementPath = getElementXpath(elementStack) + field.getKey()
 686  
                                 + "[value='" + value.toString() + "']/";
 687  
 
 688  15
                 if ("string".equalsIgnoreCase(dataType)) {
 689  10
                         validateString(value, bcb, elementPath, valResults);
 690  5
                 } else if ("integer".equalsIgnoreCase(dataType)) {
 691  1
                         validateInteger(value, bcb, elementPath, valResults);
 692  4
                 } else if ("long".equalsIgnoreCase(dataType)) {
 693  0
                         validateLong(value, bcb, elementPath, valResults);
 694  4
                 } else if ("double".equalsIgnoreCase(dataType)) {
 695  1
                         validateDouble(value, bcb, elementPath, valResults);
 696  3
                 } else if ("float".equalsIgnoreCase(dataType)) {
 697  0
                         validateFloat(value, bcb, elementPath, valResults);
 698  3
                 } else if ("boolean".equalsIgnoreCase(dataType)) {
 699  0
                         validateBoolean(value, bcb, elementPath, valResults);
 700  3
                 } else if ("date".equalsIgnoreCase(dataType)) {
 701  3
                         validateDate(value, bcb, elementPath, valResults, dateParser);
 702  
                 }
 703  15
         }
 704  
 
 705  
         private void validateBoolean(Object value, BaseConstraintBean bcb,
 706  
                         String element, List<ValidationResultInfo> results) {
 707  0
                 if (!(value instanceof Boolean)) {
 708  
                         try {
 709  0
                                 Boolean.valueOf(value.toString());
 710  0
                         } catch (Exception e) {
 711  0
                                 ValidationResultInfo val = new ValidationResultInfo(element);
 712  0
                                 val.setError(getMessage("validation.mustBeBoolean"));
 713  0
                                 results.add(val);
 714  0
                         }
 715  
                 }
 716  0
         }
 717  
 
 718  
         private void validateDouble(Object value, BaseConstraintBean bcb,
 719  
                         String element, List<ValidationResultInfo> results) {
 720  1
                 Double v = null;
 721  
 
 722  1
                 ValidationResultInfo val = new ValidationResultInfo(element);
 723  
 
 724  1
                 if (value instanceof Number) {
 725  1
                         v = ((Number) value).doubleValue();
 726  
                 } else {
 727  
                         try {
 728  0
                                 v = Double.valueOf(value.toString());
 729  0
                         } catch (Exception e) {
 730  0
                                 val.setError(getMessage("validation.mustBeDouble"));
 731  0
                         }
 732  
                 }
 733  
 
 734  1
                 if (val.isOk()) {
 735  1
                         Double maxValue = ValidatorUtils.getDouble(bcb.maxValue);
 736  1
                         Double minValue = ValidatorUtils.getDouble(bcb.minValue);
 737  
 
 738  1
                         if (maxValue != null && minValue != null) {
 739  
                                 // validate range
 740  1
                                 if (v > maxValue || v < minValue) {
 741  1
                                         val.setError(MessageUtils.interpolate(
 742  
                                                         getMessage("validation.outOfRange"), bcb.toMap()));
 743  
                                 }
 744  0
                         } else if (maxValue != null) {
 745  0
                                 if (v > maxValue) {
 746  0
                                         val.setError(MessageUtils.interpolate(
 747  
                                                         getMessage("validation.maxValueFailed"), bcb
 748  
                                                                         .toMap()));
 749  
                                 }
 750  0
                         } else if (minValue != null) {
 751  0
                                 if (v < minValue) {
 752  0
                                         val.setError(MessageUtils.interpolate(
 753  
                                                         getMessage("validation.minValueFailed"), bcb
 754  
                                                                         .toMap()));
 755  
                                 }
 756  
                         }
 757  
                 }
 758  
 
 759  1
                 if (!val.isOk()) {
 760  1
                         results.add(val);
 761  
                 }
 762  1
         }
 763  
 
 764  
         private void validateFloat(Object value, BaseConstraintBean bcb,
 765  
                         String element, List<ValidationResultInfo> results) {
 766  0
                 Float v = null;
 767  
 
 768  0
                 ValidationResultInfo val = new ValidationResultInfo(element);
 769  0
                 if (value instanceof Number) {
 770  0
                         v = ((Number) value).floatValue();
 771  
                 } else {
 772  
                         try {
 773  0
                                 v = Float.valueOf(value.toString());
 774  0
                         } catch (Exception e) {
 775  0
                                 val.setError(getMessage("validation.mustBeFloat"));
 776  0
                         }
 777  
                 }
 778  
 
 779  0
                 if (val.isOk()) {
 780  0
                         Float maxValue = ValidatorUtils.getFloat(bcb.maxValue);
 781  0
                         Float minValue = ValidatorUtils.getFloat(bcb.minValue);
 782  
 
 783  0
                         if (maxValue != null && minValue != null) {
 784  
                                 // validate range
 785  0
                                 if (v > maxValue || v < minValue) {
 786  0
                                         val.setError(MessageUtils.interpolate(
 787  
                                                         getMessage("validation.outOfRange"), bcb.toMap()));
 788  
                                 }
 789  0
                         } else if (maxValue != null) {
 790  0
                                 if (v > maxValue) {
 791  0
                                         val.setError(MessageUtils.interpolate(
 792  
                                                         getMessage("validation.maxValueFailed"), bcb
 793  
                                                                         .toMap()));
 794  
                                 }
 795  0
                         } else if (minValue != null) {
 796  0
                                 if (v < minValue) {
 797  0
                                         val.setError(MessageUtils.interpolate(
 798  
                                                         getMessage("validation.minValueFailed"), bcb
 799  
                                                                         .toMap()));
 800  
                                 }
 801  
                         }
 802  
                 }
 803  
 
 804  0
                 if (!val.isOk()) {
 805  0
                         results.add(val);
 806  
                 }
 807  0
         }
 808  
 
 809  
         private void validateLong(Object value, BaseConstraintBean bcb,
 810  
                         String element, List<ValidationResultInfo> results) {
 811  0
                 Long v = null;
 812  
 
 813  0
                 ValidationResultInfo val = new ValidationResultInfo(element);
 814  0
                 if (value instanceof Number) {
 815  0
                         v = ((Number) value).longValue();
 816  
                 } else {
 817  
                         try {
 818  0
                                 v = Long.valueOf(value.toString());
 819  0
                         } catch (Exception e) {
 820  0
                                 val.setError(getMessage("validation.mustBeLong"));
 821  0
                         }
 822  
                 }
 823  
 
 824  0
                 if (val.isOk()) {
 825  0
                         Long maxValue = ValidatorUtils.getLong(bcb.maxValue);
 826  0
                         Long minValue = ValidatorUtils.getLong(bcb.minValue);
 827  
 
 828  0
                         if (maxValue != null && minValue != null) {
 829  
                                 // validate range
 830  0
                                 if (v > maxValue || v < minValue) {
 831  0
                                         val.setError(MessageUtils.interpolate(
 832  
                                                         getMessage("validation.outOfRange"), bcb.toMap()));
 833  
                                 }
 834  0
                         } else if (maxValue != null) {
 835  0
                                 if (v > maxValue) {
 836  0
                                         val.setError(MessageUtils.interpolate(
 837  
                                                         getMessage("validation.maxValueFailed"), bcb
 838  
                                                                         .toMap()));
 839  
                                 }
 840  0
                         } else if (minValue != null) {
 841  0
                                 if (v < minValue) {
 842  0
                                         val.setError(MessageUtils.interpolate(
 843  
                                                         getMessage("validation.minValueFailed"), bcb
 844  
                                                                         .toMap()));
 845  
                                 }
 846  
                         }
 847  
                 }
 848  
 
 849  0
                 if (!val.isOk()) {
 850  0
                         results.add(val);
 851  
                 }
 852  
 
 853  0
         }
 854  
 
 855  
         private void validateInteger(Object value, BaseConstraintBean bcb,
 856  
                         String element, List<ValidationResultInfo> results) {
 857  1
                 Integer v = null;
 858  
 
 859  1
                 ValidationResultInfo val = new ValidationResultInfo(element);
 860  
 
 861  1
                 if (value instanceof Number) {
 862  0
                         v = ((Number) value).intValue();
 863  
                 } else {
 864  
                         try {
 865  1
                                 v = Integer.valueOf(value.toString());
 866  1
                         } catch (Exception e) {
 867  1
                                 val.setError(getMessage("validation.mustBeInteger"));
 868  0
                         }
 869  
                 }
 870  
 
 871  1
                 if (val.isOk()) {
 872  0
                         Integer maxValue = ValidatorUtils.getInteger(bcb.maxValue);
 873  0
                         Integer minValue = ValidatorUtils.getInteger(bcb.minValue);
 874  
 
 875  0
                         if (maxValue != null && minValue != null) {
 876  
                                 // validate range
 877  0
                                 if (v > maxValue || v < minValue) {
 878  0
                                         val.setError(MessageUtils.interpolate(
 879  
                                                         getMessage("validation.outOfRange"), bcb.toMap()));
 880  
                                 }
 881  0
                         } else if (maxValue != null) {
 882  0
                                 if (v > maxValue) {
 883  0
                                         val.setError(MessageUtils.interpolate(
 884  
                                                         getMessage("validation.maxValueFailed"), bcb
 885  
                                                                         .toMap()));
 886  
                                 }
 887  0
                         } else if (minValue != null) {
 888  0
                                 if (v < minValue) {
 889  0
                                         val.setError(MessageUtils.interpolate(
 890  
                                                         getMessage("validation.minValueFailed"), bcb
 891  
                                                                         .toMap()));
 892  
                                 }
 893  
                         }
 894  
                 }
 895  
 
 896  1
                 if (!val.isOk()) {
 897  1
                         results.add(val);
 898  
                 }
 899  1
         }
 900  
 
 901  
         private void validateDate(Object value, BaseConstraintBean bcb,
 902  
                         String element, List<ValidationResultInfo> results,
 903  
                         DateParser dateParser) {
 904  3
                 ValidationResultInfo val = new ValidationResultInfo(element);
 905  
 
 906  3
                 Date v = null;
 907  
 
 908  3
                 if (value instanceof Date) {
 909  3
                         v = (Date) value;
 910  
                 } else {
 911  
                         try {
 912  0
                                 v = dateParser.parseDate(value.toString());
 913  0
                         } catch (Exception e) {
 914  0
                                 val.setError(getMessage("validation.mustBeDate"));
 915  0
                         }
 916  
                 }
 917  
 
 918  3
                 if (val.isOk()) {
 919  3
                         Date maxValue = ValidatorUtils.getDate(bcb.maxValue, dateParser);
 920  3
                         Date minValue = ValidatorUtils.getDate(bcb.minValue, dateParser);
 921  
 
 922  3
                         if (maxValue != null && minValue != null) {
 923  
                                 // validate range
 924  0
                                 if (v.getTime() > maxValue.getTime()
 925  
                                                 || v.getTime() < minValue.getTime()) {
 926  0
                                         val.setError(MessageUtils.interpolate(
 927  
                                                         getMessage("validation.outOfRange"), bcb.toMap()));
 928  
                                 }
 929  3
                         } else if (maxValue != null) {
 930  0
                                 if (v.getTime() > maxValue.getTime()) {
 931  0
                                         val.setError(MessageUtils.interpolate(
 932  
                                                         getMessage("validation.maxValueFailed"), bcb
 933  
                                                                         .toMap()));
 934  
                                 }
 935  3
                         } else if (minValue != null) {
 936  3
                                 if (v.getTime() < minValue.getTime()) {
 937  1
                                         val.setError(MessageUtils.interpolate(
 938  
                                                         getMessage("validation.minValueFailed"), bcb
 939  
                                                                         .toMap()));
 940  
                                 }
 941  
                         }
 942  
                 }
 943  
 
 944  3
                 if (!val.isOk()) {
 945  1
                         results.add(val);
 946  
                 }
 947  3
         }
 948  
 
 949  
         private void validateString(Object value, BaseConstraintBean bcb,
 950  
                         String element, List<ValidationResultInfo> results) {
 951  
 
 952  10
                 if (value == null) {
 953  0
                         value = "";
 954  
                 }
 955  10
                 String s = value.toString().trim();
 956  
 
 957  10
                 ValidationResultInfo val = new ValidationResultInfo(element);
 958  
 
 959  10
                 Integer maxLength = tryParse(bcb.maxLength);
 960  10
                 if (maxLength != null && bcb.minLength > 0) {
 961  7
                         if (s.length() > maxLength || s.length() < bcb.minLength) {
 962  1
                                 val
 963  
                                                 .setError(MessageUtils.interpolate(
 964  
                                                                 getMessage("validation.lengthOutOfRange"), bcb
 965  
                                                                                 .toMap()));
 966  
                         }
 967  3
                 } else if (maxLength != null) {
 968  1
                         if (s.length() > Integer.parseInt(bcb.maxLength)) {
 969  1
                                 val.setError(MessageUtils.interpolate(
 970  
                                                 getMessage("validation.maxLengthFailed"), bcb.toMap()));
 971  
                         }
 972  2
                 } else if (bcb.minLength > 0) {
 973  1
                         if (s.length() < bcb.minLength) {
 974  1
                                 val.setError(MessageUtils.interpolate(
 975  
                                                 getMessage("validation.minLengthFailed"), bcb.toMap()));
 976  
                         }
 977  
                 }
 978  
 
 979  10
                 if (!val.isOk()) {
 980  3
                         results.add(val);
 981  
                 }
 982  10
         }
 983  
 
 984  
         private String getMessage(String messageId) {
 985  18
                 if (null == messageService) {
 986  18
                         return messageId;
 987  
                 }
 988  
 
 989  0
                 Message msg = messageService.getMessage(messageLocaleKey,
 990  
                                 messageGroupKey, messageId);
 991  
 
 992  0
                 return msg.getValue();
 993  
         }
 994  
 
 995  
         private String getElementXpath(Stack<String> elementStack) {
 996  49
                 StringBuilder xPath = new StringBuilder();
 997  49
                 xPath.append("/");
 998  49
                 Iterator<String> itr = elementStack.iterator();
 999  107
                 while (itr.hasNext()) {
 1000  58
                         xPath.append(itr.next() + "/");
 1001  
                 }
 1002  
 
 1003  49
                 return xPath.toString();
 1004  
         }
 1005  
 
 1006  
         /*
 1007  
          * Homemade has text so we dont need outside libs.
 1008  
          */
 1009  
         private boolean hasText(String string) {
 1010  
 
 1011  82
                 if (string == null || string.length() < 1) {
 1012  72
                         return false;
 1013  
                 }
 1014  10
                 int stringLength = string.length();
 1015  
 
 1016  10
                 for (int i = 0; i < stringLength; i++) {
 1017  10
                         char currentChar = string.charAt(i);
 1018  10
                         if (' ' != currentChar || '\t' != currentChar
 1019  
                                         || '\n' != currentChar) {
 1020  10
                                 return true;
 1021  
                         }
 1022  
                 }
 1023  
 
 1024  0
                 return false;
 1025  
         }
 1026  
 
 1027  32
         private static class BaseConstraintBean {
 1028  16
                 public boolean initialized = false;
 1029  16
                 public Integer minOccurs = 0;
 1030  16
                 public String maxOccurs = UNBOUNDED_CHECK;
 1031  16
                 public Integer minLength = 0;
 1032  16
                 public String maxLength = UNBOUNDED_CHECK;
 1033  16
                 public String dataType = null;
 1034  16
                 public String minValue = null;
 1035  16
                 public String maxValue = null;
 1036  
 
 1037  
                 public Map<String, Object> toMap() {
 1038  5
                         Map<String, Object> result = new HashMap<String, Object>();
 1039  5
                         result.put("minOccurs", minOccurs);
 1040  5
                         result.put("maxOccurs", maxOccurs);
 1041  5
                         result.put("minLength", minLength);
 1042  5
                         result.put("maxLength", maxLength);
 1043  5
                         result.put("minValue", minValue);
 1044  5
                         result.put("maxValue", maxValue);
 1045  5
                         result.put("dataType", dataType);
 1046  
 
 1047  5
                         return result;
 1048  
                 }
 1049  
         }
 1050  
 }