Coverage Report - org.kuali.rice.krad.service.impl.DictionaryValidationServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
DictionaryValidationServiceImpl
0%
0/370
0%
0/208
3.182
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krad.service.impl;
 17  
 
 18  
 import java.beans.PropertyDescriptor;
 19  
 import java.lang.reflect.InvocationTargetException;
 20  
 import java.util.Arrays;
 21  
 import java.util.Collection;
 22  
 import java.util.IdentityHashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Queue;
 28  
 import java.util.Set;
 29  
 
 30  
 import org.apache.commons.beanutils.PropertyUtils;
 31  
 import org.apache.commons.lang.ArrayUtils;
 32  
 import org.apache.commons.lang.StringUtils;
 33  
 import org.kuali.rice.core.util.RiceKeyConstants;
 34  
 import org.kuali.rice.krad.bo.BusinessObject;
 35  
 import org.kuali.rice.krad.bo.Inactivatable;
 36  
 import org.kuali.rice.krad.bo.PersistableBusinessObject;
 37  
 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
 38  
 import org.kuali.rice.krad.datadictionary.CollectionDefinition;
 39  
 import org.kuali.rice.krad.datadictionary.ComplexAttributeDefinition;
 40  
 import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
 41  
 import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase;
 42  
 import org.kuali.rice.krad.datadictionary.DataObjectEntry;
 43  
 import org.kuali.rice.krad.datadictionary.ReferenceDefinition;
 44  
 import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
 45  
 import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader;
 46  
 import org.kuali.rice.krad.datadictionary.validation.DictionaryObjectAttributeValueReader;
 47  
 import org.kuali.rice.krad.datadictionary.validation.ErrorLevel;
 48  
 import org.kuali.rice.krad.datadictionary.validation.SingleAttributeValueReader;
 49  
 import org.kuali.rice.krad.datadictionary.validation.capability.Constrainable;
 50  
 import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint;
 51  
 import org.kuali.rice.krad.datadictionary.validation.constraint.provider.ConstraintProvider;
 52  
 import org.kuali.rice.krad.datadictionary.validation.processor.CollectionConstraintProcessor;
 53  
 import org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor;
 54  
 import org.kuali.rice.krad.datadictionary.validation.result.ConstraintValidationResult;
 55  
 import org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult;
 56  
 import org.kuali.rice.krad.datadictionary.validation.result.ProcessorResult;
 57  
 import org.kuali.rice.krad.document.Document;
 58  
 import org.kuali.rice.krad.document.TransactionalDocument;
 59  
 import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
 60  
 import org.kuali.rice.krad.service.BusinessObjectService;
 61  
 import org.kuali.rice.krad.service.DataDictionaryService;
 62  
 import org.kuali.rice.krad.service.DictionaryValidationService;
 63  
 import org.kuali.rice.krad.service.DocumentDictionaryService;
 64  
 import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
 65  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 66  
 import org.kuali.rice.krad.service.PersistenceService;
 67  
 import org.kuali.rice.krad.service.PersistenceStructureService;
 68  
 import org.kuali.rice.krad.util.GlobalVariables;
 69  
 import org.kuali.rice.krad.util.MessageMap;
 70  
 import org.kuali.rice.krad.util.ObjectUtils;
 71  
 import org.kuali.rice.krad.workflow.service.WorkflowAttributePropertyResolutionService;
 72  
 
 73  
 /**
 74  
  * Validates Documents, Business Objects, and Attributes against the data dictionary. Including min, max lengths, and
 75  
  * validating expressions. This is the default, Kuali delivered implementation.
 76  
  *
 77  
  * KULRICE - 3355 Modified to prevent infinite looping (to maxDepth) scenario when a parent references a child which
 78  
  * references a parent
 79  
  *
 80  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 81  
  */
 82  0
 public class DictionaryValidationServiceImpl implements DictionaryValidationService {
 83  0
     private static org.apache.log4j.Logger LOG =
 84  
             org.apache.log4j.Logger.getLogger(DictionaryValidationServiceImpl.class);
 85  
 
 86  
     /**
 87  
      * Constant defines a validation method for an attribute value.
 88  
      * <p>Value is "validate"
 89  
      */
 90  
     public static final String VALIDATE_METHOD = "validate";
 91  
 
 92  
     protected DataDictionaryService dataDictionaryService;
 93  
     protected BusinessObjectService businessObjectService;
 94  
     protected PersistenceService persistenceService;
 95  
     protected DocumentDictionaryService documentDictionaryService;
 96  
     protected WorkflowAttributePropertyResolutionService workflowAttributePropertyResolutionService;
 97  
     protected PersistenceStructureService persistenceStructureService;
 98  
 
 99  
     @SuppressWarnings("unchecked")
 100  
     private List<CollectionConstraintProcessor> collectionConstraintProcessors;
 101  
     @SuppressWarnings("unchecked")
 102  
     private List<ConstraintProvider> constraintProviders;
 103  
     @SuppressWarnings("unchecked")
 104  
     private List<ConstraintProcessor> elementConstraintProcessors;
 105  
 
 106  
     /**
 107  
      * creates a new IdentitySet.
 108  
      *
 109  
      * @return a new Set
 110  
      */
 111  
     private static Set<BusinessObject> newIdentitySet() {
 112  0
         return java.util.Collections.newSetFromMap(new IdentityHashMap<BusinessObject, Boolean>());
 113  
     }
 114  
 
 115  
     /**
 116  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object)
 117  
      */
 118  
     public DictionaryValidationResult validate(Object object) {
 119  0
         return validate(object, object.getClass().getName(), true);
 120  
     }
 121  
 
 122  
     /**
 123  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object, boolean)
 124  
      */
 125  
     public DictionaryValidationResult validate(Object object, boolean doOptionalProcessing) {
 126  0
         return validate(object, object.getClass().getName(), doOptionalProcessing);
 127  
     }
 128  
 
 129  
     /**
 130  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object, java.lang.String)
 131  
      */
 132  
     public DictionaryValidationResult validate(Object object, String entryName) {
 133  0
         return validate(object, entryName, true);
 134  
     }
 135  
 
 136  
     /**
 137  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object, java.lang.String,
 138  
      *      boolean)
 139  
      */
 140  
     public DictionaryValidationResult validate(Object object, String entryName, boolean doOptionalProcessing) {
 141  0
         return validate(object, entryName, (String) null, doOptionalProcessing);
 142  
     }
 143  
 
 144  
     /**
 145  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object, java.lang.String,
 146  
      *      java.lang.String)
 147  
      */
 148  
     public DictionaryValidationResult validate(Object object, String entryName, String attributeName) {
 149  0
         return validate(object, entryName, attributeName, true);
 150  
     }
 151  
 
 152  
     /**
 153  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object, java.lang.String,
 154  
      *      java.lang.String, boolean)
 155  
      */
 156  
     public DictionaryValidationResult validate(Object object, String entryName, String attributeName,
 157  
             boolean doOptionalProcessing) {
 158  0
         DataDictionaryEntry entry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(entryName);
 159  0
         AttributeValueReader attributeValueReader = new DictionaryObjectAttributeValueReader(object, entryName, entry);
 160  0
         attributeValueReader.setAttributeName(attributeName);
 161  0
         return validate(attributeValueReader, doOptionalProcessing);
 162  
     }
 163  
 
 164  
     public DictionaryValidationResult validate(Object object, String entryName, DataDictionaryEntry entry,
 165  
             boolean doOptionalProcessing) {
 166  0
         AttributeValueReader attributeValueReader = new DictionaryObjectAttributeValueReader(object, entryName, entry);
 167  0
         return validate(attributeValueReader, doOptionalProcessing);
 168  
     }
 169  
 
 170  
     public void validate(String entryName, String attributeName, Object attributeValue) {
 171  0
         validate(entryName, attributeName, attributeValue, true);
 172  0
     }
 173  
 
 174  
     public void validate(String entryName, String attributeName, Object attributeValue, boolean doOptionalProcessing) {
 175  0
         AttributeDefinition attributeDefinition =
 176  
                 getDataDictionaryService().getAttributeDefinition(entryName, attributeName);
 177  
 
 178  0
         if (attributeDefinition == null) {
 179  
             // FIXME: JLR - this is what the code was doing effectively already, but seems weird not to throw an exception here if you try to validate
 180  
             // something that doesn't have an attribute definition
 181  0
             return;
 182  
         }
 183  
 
 184  0
         SingleAttributeValueReader attributeValueReader =
 185  
                 new SingleAttributeValueReader(attributeValue, entryName, attributeName, attributeDefinition);
 186  0
         validate(attributeValueReader, doOptionalProcessing);
 187  0
     }
 188  
 
 189  
     /**
 190  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDocument(org.kuali.rice.krad.document.Document)
 191  
      */
 192  
     @Override
 193  
         public void validateDocument(Document document) {
 194  0
         String documentEntryName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
 195  
 
 196  0
         validate(document, documentEntryName);
 197  0
     }
 198  
 
 199  
     /**
 200  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDocumentAttribute(org.kuali.rice.krad.document.Document,
 201  
      *      java.lang.String, java.lang.String)
 202  
      */
 203  
     @Override
 204  
         public void validateDocumentAttribute(Document document, String attributeName, String errorPrefix) {
 205  0
         String documentEntryName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
 206  
 
 207  0
         validate(document, documentEntryName, attributeName, true);
 208  0
     }
 209  
 
 210  
     public void validateDocumentAndUpdatableReferencesRecursively(Document document, int maxDepth,
 211  
             boolean validateRequired) {
 212  0
         validateDocumentAndUpdatableReferencesRecursively(document, maxDepth, validateRequired, false);
 213  0
     }
 214  
     
 215  
     public void validateDocumentAndUpdatableReferencesRecursively(Document document, int maxDepth, 
 216  
             boolean validateRequired, boolean chompLastLetterSFromCollectionName) {
 217  0
         String documentEntryName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
 218  0
         validate(document, documentEntryName);
 219  
 
 220  0
         if (maxDepth > 0) {
 221  0
             validateUpdatabableReferencesRecursively(document, maxDepth - 1, validateRequired,
 222  
                     chompLastLetterSFromCollectionName, newIdentitySet());
 223  
         }
 224  0
     }
 225  
 
 226  
     private void validateUpdatabableReferencesRecursively(BusinessObject businessObject, int maxDepth,
 227  
             boolean validateRequired, boolean chompLastLetterSFromCollectionName, Set<BusinessObject> processedBOs) {
 228  
         // if null or already processed, return
 229  0
         if (ObjectUtils.isNull(businessObject) || processedBOs.contains(businessObject)) {
 230  0
             return;
 231  
         }
 232  0
         processedBOs.add(businessObject);  // add bo to list to prevent excessive looping
 233  0
         Map<String, Class> references =
 234  
                 persistenceStructureService.listReferenceObjectFields(businessObject.getClass());
 235  0
         for (String referenceName : references.keySet()) {
 236  0
             if (persistenceStructureService.isReferenceUpdatable(businessObject.getClass(), referenceName)) {
 237  0
                 Object referenceObj = ObjectUtils.getPropertyValue(businessObject, referenceName);
 238  
 
 239  0
                 if (ObjectUtils.isNull(referenceObj) || !(referenceObj instanceof PersistableBusinessObject)) {
 240  0
                     continue;
 241  
                 }
 242  
 
 243  0
                 BusinessObject referenceBusinessObject = (BusinessObject) referenceObj;
 244  0
                 GlobalVariables.getMessageMap().addToErrorPath(referenceName);
 245  0
                 validateBusinessObject(referenceBusinessObject, validateRequired);
 246  0
                 if (maxDepth > 0) {
 247  0
                     validateUpdatabableReferencesRecursively(referenceBusinessObject, maxDepth - 1, validateRequired,
 248  
                             chompLastLetterSFromCollectionName, processedBOs);
 249  
                 }
 250  0
                 GlobalVariables.getMessageMap().removeFromErrorPath(referenceName);
 251  0
             }
 252  
         }
 253  0
         Map<String, Class> collections =
 254  
                 persistenceStructureService.listCollectionObjectTypes(businessObject.getClass());
 255  0
         for (String collectionName : collections.keySet()) {
 256  0
             if (persistenceStructureService.isCollectionUpdatable(businessObject.getClass(), collectionName)) {
 257  0
                 Object listObj = ObjectUtils.getPropertyValue(businessObject, collectionName);
 258  
 
 259  0
                 if (ObjectUtils.isNull(listObj)) {
 260  0
                     continue;
 261  
                 }
 262  
 
 263  0
                 if (!(listObj instanceof List)) {
 264  0
                     if (LOG.isInfoEnabled()) {
 265  0
                         LOG.info("The reference named " + collectionName + " of BO class " +
 266  
                                 businessObject.getClass().getName() +
 267  
                                 " should be of type java.util.List to be validated properly.");
 268  
                     }
 269  
                     continue;
 270  
                 }
 271  
 
 272  0
                 List list = (List) listObj;
 273  
 
 274  
                 //should we materialize the proxied collection or just skip validation here assuming an unmaterialized objects are valid?
 275  0
                 ObjectUtils.materializeObjects(list);
 276  
 
 277  0
                 for (int i = 0; i < list.size(); i++) {
 278  0
                     final Object o = list.get(i);
 279  0
                     if (ObjectUtils.isNotNull(o) && o instanceof PersistableBusinessObject) {
 280  0
                         final BusinessObject element = (BusinessObject) o;
 281  
 
 282  
                         final String errorPathAddition;
 283  0
                         if (chompLastLetterSFromCollectionName) {
 284  0
                             errorPathAddition =
 285  
                                     StringUtils.chomp(collectionName, "s") + "[" + Integer.toString(i) + "]";
 286  
                         } else {
 287  0
                             errorPathAddition = collectionName + "[" + Integer.toString(i) + "]";
 288  
                         }
 289  
 
 290  0
                         GlobalVariables.getMessageMap().addToErrorPath(errorPathAddition);
 291  0
                         validateBusinessObject(element, validateRequired);
 292  0
                         if (maxDepth > 0) {
 293  0
                             validateUpdatabableReferencesRecursively(element, maxDepth - 1, validateRequired,
 294  
                                     chompLastLetterSFromCollectionName, processedBOs);
 295  
                         }
 296  0
                         GlobalVariables.getMessageMap().removeFromErrorPath(errorPathAddition);
 297  
                     }
 298  
                 }
 299  0
             }
 300  
         }
 301  0
     }
 302  
 
 303  
     /**
 304  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#isBusinessObjectValid(org.kuali.rice.krad.bo.BusinessObject)
 305  
      */
 306  
     public boolean isBusinessObjectValid(BusinessObject businessObject) {
 307  0
         return isBusinessObjectValid(businessObject, null);
 308  
     }
 309  
 
 310  
     /**
 311  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#isBusinessObjectValid(org.kuali.rice.krad.bo.BusinessObject,
 312  
      *      String)
 313  
      */
 314  
     public boolean isBusinessObjectValid(BusinessObject businessObject, String prefix) {
 315  0
         final MessageMap errorMap = GlobalVariables.getMessageMap();
 316  0
         int originalErrorCount = errorMap.getErrorCount();
 317  
 
 318  0
         errorMap.addToErrorPath(prefix);
 319  0
         validateBusinessObject(businessObject);
 320  0
         errorMap.removeFromErrorPath(prefix);
 321  
 
 322  0
         return errorMap.getErrorCount() == originalErrorCount;
 323  
     }
 324  
 
 325  
     /**
 326  
      * @param businessObject - business object to validate
 327  
      */
 328  
     public void validateBusinessObjectsRecursively(BusinessObject businessObject, int depth) {
 329  0
         if (ObjectUtils.isNull(businessObject)) {
 330  0
             return;
 331  
         }
 332  
 
 333  
         // validate primitives and any specific bo validation
 334  0
         validateBusinessObject(businessObject);
 335  
 
 336  
         // call method to recursively find business objects and validate
 337  0
         validateBusinessObjectsFromDescriptors(businessObject,
 338  
                 PropertyUtils.getPropertyDescriptors(businessObject.getClass()), depth);
 339  0
     }
 340  
 
 341  
     /**
 342  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateBusinessObject(org.kuali.rice.krad.bo.BusinessObject)
 343  
      */
 344  
     @Override
 345  
     public void validateBusinessObject(BusinessObject businessObject) {
 346  0
         validateBusinessObject(businessObject, true);
 347  0
     }
 348  
 
 349  
     /**
 350  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateBusinessObject(org.kuali.rice.krad.bo.BusinessObject,
 351  
      *      boolean)
 352  
      */
 353  
     @Override
 354  
     public void validateBusinessObject(BusinessObject businessObject, boolean validateRequired) {
 355  0
         if (ObjectUtils.isNull(businessObject)) {
 356  0
             return;
 357  
         }
 358  
 
 359  0
         validate(businessObject, businessObject.getClass().getName());
 360  0
     }
 361  
 
 362  
     /**
 363  
      * iterates through the property discriptors looking for business objects or lists of business objects. calls
 364  
      * validate method
 365  
      * for each bo found
 366  
      *
 367  
      * @param object
 368  
      * @param propertyDescriptors
 369  
      */
 370  
     protected void validateBusinessObjectsFromDescriptors(Object object, PropertyDescriptor[] propertyDescriptors,
 371  
             int depth) {
 372  0
         for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
 373  
             // validate the properties that are descended from BusinessObject
 374  0
             if (propertyDescriptor.getPropertyType() != null &&
 375  
                     PersistableBusinessObject.class.isAssignableFrom(propertyDescriptor.getPropertyType()) &&
 376  
                     ObjectUtils.getPropertyValue(object, propertyDescriptor.getName()) != null) {
 377  0
                 BusinessObject bo = (BusinessObject) ObjectUtils.getPropertyValue(object, propertyDescriptor.getName());
 378  0
                 if (depth == 0) {
 379  0
                     GlobalVariables.getMessageMap().addToErrorPath(propertyDescriptor.getName());
 380  0
                     validateBusinessObject(bo);
 381  0
                     GlobalVariables.getMessageMap().removeFromErrorPath(propertyDescriptor.getName());
 382  
                 } else {
 383  0
                     validateBusinessObjectsRecursively(bo, depth - 1);
 384  
                 }
 385  0
             }
 386  
 
 387  
             /*
 388  
              * if property is a List, then walk the list and do the validation on each contained object that is a descendent of
 389  
              * BusinessObject
 390  
              */
 391  0
             else if (propertyDescriptor.getPropertyType() != null &&
 392  
                     (List.class).isAssignableFrom(propertyDescriptor.getPropertyType()) &&
 393  
                     ObjectUtils.getPropertyValue(object, propertyDescriptor.getName()) != null) {
 394  0
                 List propertyList = (List) ObjectUtils.getPropertyValue(object, propertyDescriptor.getName());
 395  0
                 for (int j = 0; j < propertyList.size(); j++) {
 396  0
                     if (propertyList.get(j) != null && propertyList.get(j) instanceof PersistableBusinessObject) {
 397  0
                         if (depth == 0) {
 398  0
                             GlobalVariables.getMessageMap().addToErrorPath(
 399  
                                     StringUtils.chomp(propertyDescriptor.getName(), "s") + "[" +
 400  
                                             (new Integer(j)).toString() + "]");
 401  0
                             validateBusinessObject((BusinessObject) propertyList.get(j));
 402  0
                             GlobalVariables.getMessageMap().removeFromErrorPath(
 403  
                                     StringUtils.chomp(propertyDescriptor.getName(), "s") + "[" +
 404  
                                             (new Integer(j)).toString() + "]");
 405  
                         } else {
 406  0
                             validateBusinessObjectsRecursively((BusinessObject) propertyList.get(j), depth - 1);
 407  
                         }
 408  
                     }
 409  
                 }
 410  
             }
 411  
         }
 412  0
     }
 413  
 
 414  
     /**
 415  
      * calls validate format and required check for the given propertyDescriptor
 416  
      *
 417  
      * @param entryName
 418  
      * @param object
 419  
      * @param propertyDescriptor
 420  
      * @param errorPrefix
 421  
      * @deprecated since 1.1
 422  
      */
 423  
     @Deprecated
 424  
     public void validatePrimitiveFromDescriptor(String entryName, Object object, PropertyDescriptor propertyDescriptor,
 425  
             String errorPrefix, boolean validateRequired) {
 426  
 
 427  
         // validate the primitive attributes if defined in the dictionary
 428  0
         if (null != propertyDescriptor) {
 429  0
             validate(object, entryName, propertyDescriptor.getName(), validateRequired);
 430  
         }
 431  0
     }
 432  
 
 433  
     /**
 434  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExists(org.kuali.rice.krad.bo.BusinessObject,
 435  
      *      org.kuali.rice.krad.datadictionary.ReferenceDefinition)
 436  
      */
 437  
     public boolean validateReferenceExists(BusinessObject bo, ReferenceDefinition reference) {
 438  0
         return validateReferenceExists(bo, reference.getAttributeName());
 439  
     }
 440  
 
 441  
     /**
 442  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExists(org.kuali.rice.krad.bo.BusinessObject,
 443  
      *      java.lang.String)
 444  
      */
 445  
     public boolean validateReferenceExists(BusinessObject bo, String referenceName) {
 446  
 
 447  
         // attempt to retrieve the specified object from the db
 448  0
         BusinessObject referenceBo = businessObjectService.getReferenceIfExists(bo, referenceName);
 449  
 
 450  
         // if it isn't there, then it doesn't exist, return false
 451  0
         if (ObjectUtils.isNotNull(referenceBo)) {
 452  0
             return true;
 453  
         }
 454  
 
 455  
         // otherwise, it is there, return true
 456  0
         return false;
 457  
     }
 458  
 
 459  
     /**
 460  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceIsActive(org.kuali.rice.krad.bo.BusinessObject,
 461  
      *      org.kuali.rice.krad.datadictionary.ReferenceDefinition)
 462  
      */
 463  
     public boolean validateReferenceIsActive(BusinessObject bo, ReferenceDefinition reference) {
 464  0
         return validateReferenceIsActive(bo, reference.getAttributeName());
 465  
     }
 466  
 
 467  
     /**
 468  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceIsActive(org.kuali.rice.krad.bo.BusinessObject,
 469  
      *      java.lang.String, java.lang.String, boolean)
 470  
      */
 471  
     public boolean validateReferenceIsActive(BusinessObject bo, String referenceName) {
 472  
 
 473  
         // attempt to retrieve the specified object from the db
 474  0
         BusinessObject referenceBo = businessObjectService.getReferenceIfExists(bo, referenceName);
 475  0
         if (referenceBo == null) {
 476  0
             return false;
 477  
         }
 478  0
         if (!(referenceBo instanceof Inactivatable) || ((Inactivatable) referenceBo).isActive()) {
 479  0
             return true;
 480  
         }
 481  
 
 482  0
         return false;
 483  
     }
 484  
 
 485  
     /**
 486  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExistsAndIsActive(org.kuali.rice.krad.bo.BusinessObject,
 487  
      *      org.kuali.rice.krad.datadictionary.ReferenceDefinition)
 488  
      */
 489  
     public boolean validateReferenceExistsAndIsActive(BusinessObject bo, ReferenceDefinition reference) {
 490  0
         boolean success = true;
 491  
         // intelligently use the fieldname from the reference, or get it out
 492  
         // of the dataDictionaryService
 493  
         String displayFieldName;
 494  0
         if (reference.isDisplayFieldNameSet()) {
 495  0
             displayFieldName = reference.getDisplayFieldName();
 496  
         } else {
 497  0
             Class<?> boClass =
 498  
                     reference.isCollectionReference() ? reference.getCollectionBusinessObjectClass() : bo.getClass();
 499  0
             displayFieldName =
 500  
                     dataDictionaryService.getAttributeLabel(boClass, reference.getAttributeToHighlightOnFail());
 501  
         }
 502  
 
 503  0
         if (reference.isCollectionReference()) {
 504  0
             success = validateCollectionReferenceExistsAndIsActive(bo, reference, displayFieldName,
 505  
                     StringUtils.split(reference.getCollection(), "."), null);
 506  
         } else {
 507  0
             success = validateReferenceExistsAndIsActive(bo, reference.getAttributeName(),
 508  
                     reference.getAttributeToHighlightOnFail(), displayFieldName);
 509  
         }
 510  0
         return success;
 511  
     }
 512  
 
 513  
     /**
 514  
      * @param bo the object to get the collection from
 515  
      * @param reference the <code>ReferenceDefinition</code> of the collection to validate
 516  
      * @param displayFieldName the name of the field
 517  
      * @param intermediateCollections array containing the path to the collection as tokens
 518  
      * @param pathToAttribute the rebuilt path to the ReferenceDefinition.attributeToHighlightOnFail which includes the
 519  
      * index of
 520  
      * each subcollection
 521  
      * @return
 522  
      */
 523  
     private boolean validateCollectionReferenceExistsAndIsActive(BusinessObject bo, ReferenceDefinition reference,
 524  
             String displayFieldName, String[] intermediateCollections, String pathToAttributeI) {
 525  0
         boolean success = true;
 526  
         Collection<PersistableBusinessObject> referenceCollection;
 527  0
         String collectionName = intermediateCollections[0];
 528  
         // remove current collection from intermediates
 529  0
         intermediateCollections = (String[]) ArrayUtils.removeElement(intermediateCollections, collectionName);
 530  
         try {
 531  0
             referenceCollection = (Collection) PropertyUtils.getProperty(bo, collectionName);
 532  0
         } catch (Exception e) {
 533  0
             throw new RuntimeException(e);
 534  0
         }
 535  0
         int pos = 0;
 536  0
         Iterator<PersistableBusinessObject> iterator = referenceCollection.iterator();
 537  0
         while (iterator.hasNext()) {
 538  0
             String pathToAttribute =
 539  
                     StringUtils.defaultString(pathToAttributeI) + collectionName + "[" + (pos++) + "].";
 540  
             // keep drilling down until we reach the nested collection we want
 541  0
             if (intermediateCollections.length > 0) {
 542  0
                 success &= validateCollectionReferenceExistsAndIsActive(iterator.next(), reference, displayFieldName,
 543  
                         intermediateCollections, pathToAttribute);
 544  
             } else {
 545  0
                 String attributeToHighlightOnFail = pathToAttribute + reference.getAttributeToHighlightOnFail();
 546  0
                 success &= validateReferenceExistsAndIsActive(iterator.next(), reference.getAttributeName(),
 547  
                         attributeToHighlightOnFail, displayFieldName);
 548  
             }
 549  0
         }
 550  
 
 551  0
         return success;
 552  
     }
 553  
 
 554  
     /**
 555  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExistsAndIsActive(org.kuali.rice.krad.bo.BusinessObject,
 556  
      *      java.lang.String, java.lang.String, boolean, boolean, java.lang.String, java.lang.String)
 557  
      */
 558  
 
 559  
     public boolean validateReferenceExistsAndIsActive(BusinessObject bo, String referenceName,
 560  
             String attributeToHighlightOnFail, String displayFieldName) {
 561  
 
 562  
         // if we're dealing with a nested attribute, we need to resolve down to the BO where the primitive attribute is located
 563  
         // this is primarily to deal with the case of a defaultExistenceCheck that uses an "extension", i.e referenceName
 564  
         // would be extension.attributeName
 565  0
         if (ObjectUtils.isNestedAttribute(referenceName)) {
 566  0
             String nestedAttributePrefix = ObjectUtils.getNestedAttributePrefix(referenceName);
 567  0
             String nestedAttributePrimitive = ObjectUtils.getNestedAttributePrimitive(referenceName);
 568  0
             Object nestedObject = ObjectUtils.getPropertyValue(bo, nestedAttributePrefix);
 569  0
             if (!(nestedObject instanceof BusinessObject)) {
 570  0
                 throw new ObjectNotABusinessObjectRuntimeException(
 571  
                         "Attribute requested (" + nestedAttributePrefix + ") is of class: " + "'" +
 572  
                                 nestedObject.getClass().getName() + "' and is not a " +
 573  
                                 "descendent of BusinessObject.");
 574  
             }
 575  0
             return validateReferenceExistsAndIsActive((BusinessObject) nestedObject, nestedAttributePrimitive,
 576  
                     attributeToHighlightOnFail, displayFieldName);
 577  
         }
 578  
 
 579  0
         boolean success = true;
 580  
         boolean exists;
 581  
         boolean active;
 582  
 
 583  0
         boolean fkFieldsPopulated = true;
 584  
         // need to check for DD relationship FKs
 585  0
         List<String> fkFields =
 586  
                 getDataDictionaryService().getRelationshipSourceAttributes(bo.getClass().getName(), referenceName);
 587  0
         if (fkFields != null) {
 588  0
             for (String fkFieldName : fkFields) {
 589  0
                 Object fkFieldValue = null;
 590  
                 try {
 591  0
                     fkFieldValue = PropertyUtils.getProperty(bo, fkFieldName);
 592  
                 }
 593  
                 // if we cant retrieve the field value, then
 594  
                 // it doesnt have a value
 595  0
                 catch (IllegalAccessException e) {
 596  0
                     fkFieldsPopulated = false;
 597  0
                 } catch (InvocationTargetException e) {
 598  0
                     fkFieldsPopulated = false;
 599  0
                 } catch (NoSuchMethodException e) {
 600  0
                     fkFieldsPopulated = false;
 601  0
                 }
 602  
 
 603  
                 // test the value
 604  0
                 if (fkFieldValue == null) {
 605  0
                     fkFieldsPopulated = false;
 606  0
                 } else if (String.class.isAssignableFrom(fkFieldValue.getClass())) {
 607  0
                     if (StringUtils.isBlank((String) fkFieldValue)) {
 608  0
                         fkFieldsPopulated = false;
 609  
                     }
 610  
                 }
 611  0
             }
 612  0
         } else if (bo instanceof PersistableBusinessObject) { // if no DD relationship exists, check the persistence service
 613  0
             fkFieldsPopulated = persistenceService
 614  
                     .allForeignKeyValuesPopulatedForReference((PersistableBusinessObject) bo, referenceName);
 615  
         }
 616  
 
 617  
         // only bother if all the fk fields have values
 618  0
         if (fkFieldsPopulated) {
 619  
 
 620  
             // do the existence test
 621  0
             exists = validateReferenceExists(bo, referenceName);
 622  0
             if (exists) {
 623  
 
 624  
                 // do the active test, if appropriate
 625  0
                 if (!(bo instanceof Inactivatable) || ((Inactivatable) bo).isActive()) {
 626  0
                     active = validateReferenceIsActive(bo, referenceName);
 627  0
                     if (!active) {
 628  0
                         GlobalVariables.getMessageMap()
 629  
                                 .putError(attributeToHighlightOnFail, RiceKeyConstants.ERROR_INACTIVE,
 630  
                                         displayFieldName);
 631  0
                         success &= false;
 632  
                     }
 633  
                 }
 634  
             } else {
 635  0
                 GlobalVariables.getMessageMap()
 636  
                         .putError(attributeToHighlightOnFail, RiceKeyConstants.ERROR_EXISTENCE, displayFieldName);
 637  0
                 success &= false;
 638  
             }
 639  
         }
 640  0
         return success;
 641  
     }
 642  
 
 643  
     /**
 644  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecks(org.kuali.rice.krad.bo.BusinessObject)
 645  
      */
 646  
     public boolean validateDefaultExistenceChecks(BusinessObject bo) {
 647  
 
 648  0
         boolean success = true;
 649  
 
 650  
         // get a collection of all the referenceDefinitions setup for this object
 651  0
         Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(bo.getClass());
 652  
 
 653  
         // walk through the references, doing the tests on each
 654  0
         for (Iterator iter = references.iterator(); iter.hasNext(); ) {
 655  0
             ReferenceDefinition reference = (ReferenceDefinition) iter.next();
 656  
 
 657  
             // do the existence and validation testing
 658  0
             success &= validateReferenceExistsAndIsActive(bo, reference);
 659  0
         }
 660  0
         return success;
 661  
     }
 662  
 
 663  
     /**
 664  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecksForNewCollectionItem(org.kuali.rice.krad.bo.BusinessObject,
 665  
      *      org.kuali.rice.krad.bo.BusinessObject, java.lang.String)
 666  
      */
 667  
     public boolean validateDefaultExistenceChecksForNewCollectionItem(BusinessObject bo,
 668  
             BusinessObject newCollectionItem, String collectionName) {
 669  0
         boolean success = true;
 670  
 
 671  0
         if (StringUtils.isNotBlank(collectionName)) {
 672  
             // get a collection of all the referenceDefinitions setup for this object
 673  0
             Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(bo.getClass());
 674  
 
 675  
             // walk through the references, doing the tests on each
 676  0
             for (Iterator iter = references.iterator(); iter.hasNext(); ) {
 677  0
                 ReferenceDefinition reference = (ReferenceDefinition) iter.next();
 678  0
                 if (collectionName != null && collectionName.equals(reference.getCollection())) {
 679  
                     String displayFieldName;
 680  0
                     if (reference.isDisplayFieldNameSet()) {
 681  0
                         displayFieldName = reference.getDisplayFieldName();
 682  
                     } else {
 683  0
                         Class boClass =
 684  
                                 reference.isCollectionReference() ? reference.getCollectionBusinessObjectClass() :
 685  
                                         bo.getClass();
 686  0
                         displayFieldName = dataDictionaryService
 687  
                                 .getAttributeLabel(boClass, reference.getAttributeToHighlightOnFail());
 688  
                     }
 689  
 
 690  0
                     success &= validateReferenceExistsAndIsActive(newCollectionItem, reference.getAttributeName(),
 691  
                             reference.getAttributeToHighlightOnFail(), displayFieldName);
 692  
                 }
 693  0
             }
 694  
         }
 695  
 
 696  0
         return success;
 697  
     }
 698  
 
 699  
     /**
 700  
      * This overridden method ...
 701  
      *
 702  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecksForTransDoc(org.kuali.rice.krad.document.TransactionalDocument)
 703  
      */
 704  
     public boolean validateDefaultExistenceChecksForTransDoc(TransactionalDocument document) {
 705  0
         boolean success = true;
 706  
 
 707  
         // get a collection of all the referenceDefinitions setup for this object
 708  0
         Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(document);
 709  
 
 710  
         // walk through the references, doing the tests on each
 711  0
         for (Iterator iter = references.iterator(); iter.hasNext(); ) {
 712  0
             ReferenceDefinition reference = (ReferenceDefinition) iter.next();
 713  
 
 714  
             // do the existence and validation testing
 715  0
             success &= validateReferenceExistsAndIsActive(document, reference);
 716  0
         }
 717  0
         return success;
 718  
     }
 719  
 
 720  
     /**
 721  
      * This overridden method ...
 722  
      *
 723  
      * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecksForNewCollectionItem(org.kuali.rice.krad.document.TransactionalDocument,
 724  
      *      org.kuali.rice.krad.bo.PersistableBusinessObject)
 725  
      */
 726  
     public boolean validateDefaultExistenceChecksForNewCollectionItem(TransactionalDocument document,
 727  
             BusinessObject newCollectionItem, String collectionName) {
 728  0
         boolean success = true;
 729  0
         if (StringUtils.isNotBlank(collectionName)) {
 730  
             // get a collection of all the referenceDefinitions setup for this object
 731  0
             Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(document);
 732  
 
 733  
             // walk through the references, doing the tests on each
 734  0
             for (Iterator iter = references.iterator(); iter.hasNext(); ) {
 735  0
                 ReferenceDefinition reference = (ReferenceDefinition) iter.next();
 736  0
                 if (collectionName != null && collectionName.equals(reference.getCollection())) {
 737  
                     String displayFieldName;
 738  0
                     if (reference.isDisplayFieldNameSet()) {
 739  0
                         displayFieldName = reference.getDisplayFieldName();
 740  
                     } else {
 741  0
                         Class boClass =
 742  
                                 reference.isCollectionReference() ? reference.getCollectionBusinessObjectClass() :
 743  
                                         document.getClass();
 744  0
                         displayFieldName = dataDictionaryService
 745  
                                 .getAttributeLabel(boClass, reference.getAttributeToHighlightOnFail());
 746  
                     }
 747  
 
 748  0
                     success &= validateReferenceExistsAndIsActive(newCollectionItem, reference.getAttributeName(),
 749  
                             reference.getAttributeToHighlightOnFail(), displayFieldName);
 750  
                 }
 751  0
             }
 752  
         }
 753  0
         return success;
 754  
     }
 755  
 
 756  
     /*
 757  
     * 1.1 validation methods
 758  
     */
 759  
 
 760  
     /*
 761  
       * This is the top-level validation method for all attribute value readers
 762  
       */
 763  
     public DictionaryValidationResult validate(AttributeValueReader valueReader, boolean doOptionalProcessing) {
 764  
 
 765  0
         DictionaryValidationResult result = new DictionaryValidationResult();
 766  
 
 767  0
         if (valueReader.getAttributeName() == null) {
 768  0
             validateObject(result, valueReader, doOptionalProcessing);
 769  
         } else {
 770  0
             validateAttribute(result, valueReader, doOptionalProcessing);
 771  
         }
 772  
 
 773  0
         if (result.getNumberOfErrors() > 0) {
 774  0
             for (Iterator<ConstraintValidationResult> iterator = result.iterator(); iterator.hasNext(); ) {
 775  0
                 ConstraintValidationResult constraintValidationResult = iterator.next();
 776  0
                 if (constraintValidationResult.getStatus().getLevel() >= ErrorLevel.WARN.getLevel())
 777  0
                     setFieldError(constraintValidationResult.getEntryName(),
 778  
                             constraintValidationResult.getAttributeName(), constraintValidationResult.getErrorKey(),
 779  
                             constraintValidationResult.getErrorParameters());
 780  0
             }
 781  
         }
 782  
 
 783  0
         return result;
 784  
     }
 785  
 
 786  
     private void processElementConstraints(DictionaryValidationResult result, Object value, Constrainable definition,
 787  
             AttributeValueReader attributeValueReader, boolean doOptionalProcessing) {
 788  0
         processConstraints(result, elementConstraintProcessors, value, definition, attributeValueReader,
 789  
                 doOptionalProcessing);
 790  0
     }
 791  
 
 792  
     private void processCollectionConstraints(DictionaryValidationResult result, Collection<?> collection,
 793  
             Constrainable definition, AttributeValueReader attributeValueReader, boolean doOptionalProcessing) {
 794  0
         processConstraints(result, collectionConstraintProcessors, collection, definition, attributeValueReader,
 795  
                 doOptionalProcessing);
 796  0
     }
 797  
 
 798  
     @SuppressWarnings("unchecked")
 799  
     private void processConstraints(DictionaryValidationResult result,
 800  
             List<? extends ConstraintProcessor> constraintProcessors, Object value, Constrainable definition,
 801  
             AttributeValueReader attributeValueReader, boolean doOptionalProcessing) {
 802  
         //TODO: Implement custom validators
 803  
 
 804  0
         if (constraintProcessors != null) {
 805  0
             Constrainable selectedDefinition = definition;
 806  0
             AttributeValueReader selectedAttributeValueReader = attributeValueReader;
 807  
 
 808  
             // First - take the constrainable definition and get its constraints
 809  
 
 810  0
             Queue<Constraint> constraintQueue = new LinkedList<Constraint>();
 811  
 
 812  
             // Using a for loop to iterate through constraint processors because ordering is important
 813  0
             for (ConstraintProcessor<Object, Constraint> processor : constraintProcessors) {
 814  
 
 815  
                 // Let the calling method opt out of any optional processing
 816  0
                 if (!doOptionalProcessing && processor.isOptional()) {
 817  0
                     result.addSkipped(attributeValueReader, processor.getName());
 818  0
                     continue;
 819  
                 }
 820  
 
 821  0
                 Class<? extends Constraint> constraintType = processor.getConstraintType();
 822  
 
 823  
                 // Add all of the constraints for this constraint type for all providers to the queue
 824  0
                 for (ConstraintProvider constraintProvider : constraintProviders) {
 825  0
                     if (constraintProvider.isSupported(selectedDefinition)) {
 826  0
                         Collection<Constraint> constraintList =
 827  
                                 constraintProvider.getConstraints(selectedDefinition, constraintType);
 828  0
                         if (constraintList != null)
 829  0
                             constraintQueue.addAll(constraintList);
 830  0
                     }
 831  
                 }
 832  
 
 833  
                 // If there are no constraints provided for this definition, then just skip it
 834  0
                 if (constraintQueue.isEmpty()) {
 835  0
                     result.addSkipped(attributeValueReader, processor.getName());
 836  0
                     continue;
 837  
                 }
 838  
 
 839  0
                 Collection<Constraint> additionalConstraints = new LinkedList<Constraint>();
 840  
 
 841  
                 // This loop is functionally identical to a for loop, but it has the advantage of letting us keep the queue around
 842  
                 // and populate it with any new constraints contributed by the processor
 843  0
                 while (!constraintQueue.isEmpty()) {
 844  
 
 845  0
                     Constraint constraint = constraintQueue.poll();
 846  
 
 847  
                     // If this constraint is not one that this process handles, then skip and add to the queue for the next processor;
 848  
                     // obviously this would be redundant (we're only looking at constraints that this processor can process) except that
 849  
                     // the previous processor might have stuck a new constraint (or constraints) on the queue
 850  0
                     if (!constraintType.isInstance(constraint)) {
 851  0
                         result.addSkipped(attributeValueReader, processor.getName());
 852  0
                         additionalConstraints.add(constraint);
 853  0
                         continue;
 854  
                     }
 855  
 
 856  0
                     ProcessorResult processorResult =
 857  
                             processor.process(result, value, constraint, selectedAttributeValueReader);
 858  
 
 859  0
                     Collection<Constraint> processorResultContraints = processorResult.getConstraints();
 860  0
                     if (processorResultContraints != null && processorResultContraints.size() > 0)
 861  0
                         additionalConstraints.addAll(processorResultContraints);
 862  
 
 863  
                     // Change the selected definition to whatever was returned from the processor
 864  0
                     if (processorResult.isDefinitionProvided())
 865  0
                         selectedDefinition = processorResult.getDefinition();
 866  
                     // Change the selected attribute value reader to whatever was returned from the processor
 867  0
                     if (processorResult.isAttributeValueReaderProvided())
 868  0
                         selectedAttributeValueReader = processorResult.getAttributeValueReader();
 869  0
                 }
 870  
 
 871  
                 // After iterating through all the constraints for this processor, add additional constraints for following processors
 872  0
                 constraintQueue.addAll(additionalConstraints);
 873  0
             }
 874  
         }
 875  0
     }
 876  
 
 877  
     private void setFieldError(String entryName, String attributeName, String key, String... args) {
 878  0
         if (getDataDictionaryService() == null)
 879  0
             return;
 880  
 
 881  0
         String errorLabel = getDataDictionaryService().getAttributeErrorLabel(entryName, attributeName);
 882  
         // FIXME: There's got to be a cleaner way of doing this.
 883  0
         List<String> list = new LinkedList<String>();
 884  0
         list.add(errorLabel);
 885  0
         list.addAll(Arrays.asList(args));
 886  0
         String[] array = new String[list.size()];
 887  0
         array = list.toArray(array);
 888  0
         GlobalVariables.getMessageMap().putError(attributeName, key, array);
 889  0
     }
 890  
 
 891  
     private void validateAttribute(DictionaryValidationResult result, AttributeValueReader attributeValueReader,
 892  
             boolean checkIfRequired) throws AttributeValidationException {
 893  0
         Constrainable definition = attributeValueReader.getDefinition(attributeValueReader.getAttributeName());
 894  0
         validateAttribute(result, definition, attributeValueReader, checkIfRequired);
 895  0
     }
 896  
 
 897  
     private void validateAttribute(DictionaryValidationResult result, Constrainable definition,
 898  
             AttributeValueReader attributeValueReader, boolean checkIfRequired) throws AttributeValidationException {
 899  
 
 900  0
         if (definition == null)
 901  0
             throw new AttributeValidationException(
 902  
                     "Unable to validate constraints for attribute \"" + attributeValueReader.getAttributeName() +
 903  
                             "\" on entry \"" + attributeValueReader.getEntryName() +
 904  
                             "\" because no attribute definition can be found.");
 905  
 
 906  0
         Object value = attributeValueReader.getValue();
 907  
 
 908  0
         processElementConstraints(result, value, definition, attributeValueReader, checkIfRequired);
 909  0
     }
 910  
 
 911  
     private void validateObject(DictionaryValidationResult result, AttributeValueReader attributeValueReader,
 912  
             boolean doOptionalProcessing) throws AttributeValidationException {
 913  
 
 914  
         // If the entry itself is constrainable then the attribute value reader will return it here and we'll need to check if it has any constraints
 915  
         //FIXME: WJG - Do we want to make entry be constrainable?
 916  0
         Constrainable objectEntry = attributeValueReader.getEntry();
 917  0
         processElementConstraints(result, attributeValueReader.getObject(), objectEntry, attributeValueReader,
 918  
                 doOptionalProcessing);
 919  
 
 920  0
         List<Constrainable> definitions = attributeValueReader.getDefinitions();
 921  
 
 922  
         // Exit if the attribute value reader has no child definitions
 923  0
         if (null == definitions)
 924  0
             return;
 925  
 
 926  
         //Process all attribute definitions
 927  0
         for (Constrainable definition : definitions) {
 928  0
             String attributeName = definition.getName();
 929  0
             attributeValueReader.setAttributeName(attributeName);
 930  0
             Object value = attributeValueReader.getValue(attributeName);
 931  
 
 932  0
             processElementConstraints(result, value, definition, attributeValueReader, doOptionalProcessing);
 933  0
         }
 934  
 
 935  
         //Process any constraints that may be defined on complex attributes
 936  0
         if (objectEntry instanceof DataDictionaryEntryBase) {
 937  0
             List<ComplexAttributeDefinition> complexAttrDefinitions =
 938  
                     ((DataDictionaryEntryBase) objectEntry).getComplexAttributes();
 939  
 
 940  0
             if (complexAttrDefinitions != null) {
 941  0
                 for (ComplexAttributeDefinition complexAttrDefinition : complexAttrDefinitions) {
 942  0
                     DataDictionaryEntry childEntry = complexAttrDefinition.getDataObjectEntry();
 943  
 
 944  0
                     String attributeName = complexAttrDefinition.getName();
 945  
 
 946  0
                     attributeValueReader.setAttributeName(attributeName);
 947  0
                     Object value = attributeValueReader.getValue();
 948  0
                     if (value != null) {
 949  0
                         AttributeValueReader nestedAttributeValueReader =
 950  
                                 new DictionaryObjectAttributeValueReader(value, childEntry.getFullClassName(),
 951  
                                         childEntry, attributeValueReader.getPath());
 952  0
                         validateObject(result, nestedAttributeValueReader, doOptionalProcessing);
 953  
                     }
 954  
 
 955  0
                     processElementConstraints(result, value, complexAttrDefinition, attributeValueReader,
 956  
                             doOptionalProcessing);
 957  0
                 }
 958  
             }
 959  
         }
 960  
 
 961  
         //FIXME: I think we may want to use a new CollectionConstrainable interface instead to obtain from
 962  
         //DictionaryObjectAttributeValueReader
 963  0
         DataObjectEntry entry = (DataObjectEntry) attributeValueReader.getEntry();
 964  0
         if (entry != null) {
 965  0
             for (CollectionDefinition collectionDefinition : entry.getCollections()) {
 966  
                 //TODO: Do we need to be able to handle simple collections (ie. String, etc)
 967  
 
 968  0
                 String childEntryName = collectionDefinition.getDataObjectClass();
 969  0
                 String attributeName = collectionDefinition.getName();
 970  0
                 attributeValueReader.setAttributeName(attributeName);
 971  0
                 Collection<?> collectionObject = attributeValueReader.getValue();
 972  0
                 DataDictionaryEntry childEntry = childEntryName != null ?
 973  
                         getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(childEntryName) : null;
 974  0
                 if (collectionObject != null) {
 975  0
                     for (Object value : collectionObject) {
 976  
                         //FIXME: It's inefficient to be creating new attribute reader for each item in collection
 977  0
                         AttributeValueReader nestedAttributeValueReader =
 978  
                                 new DictionaryObjectAttributeValueReader(value, childEntryName, childEntry,
 979  
                                         attributeValueReader.getPath());
 980  0
                         validateObject(result, nestedAttributeValueReader, doOptionalProcessing);
 981  0
                     }
 982  
                 }
 983  
 
 984  0
                 processCollectionConstraints(result, collectionObject, collectionDefinition, attributeValueReader,
 985  
                         doOptionalProcessing);
 986  0
             }
 987  
         }
 988  0
     }
 989  
 
 990  
     /**
 991  
      * @return Returns the dataDictionaryService.
 992  
      */
 993  
     public DataDictionaryService getDataDictionaryService() {
 994  0
         return dataDictionaryService;
 995  
     }
 996  
 
 997  
     /**
 998  
      * @param dataDictionaryService The dataDictionaryService to set.
 999  
      */
 1000  
     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
 1001  0
         this.dataDictionaryService = dataDictionaryService;
 1002  0
     }
 1003  
 
 1004  
     /**
 1005  
      * Sets the businessObjectService attribute value.
 1006  
      *
 1007  
      * @param businessObjectService The businessObjectService to set.
 1008  
      */
 1009  
     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
 1010  0
         this.businessObjectService = businessObjectService;
 1011  0
     }
 1012  
 
 1013  
     /**
 1014  
      * Sets the persistenceService attribute value.
 1015  
      *
 1016  
      * @param persistenceService The persistenceService to set.
 1017  
      */
 1018  
     public void setPersistenceService(PersistenceService persistenceService) {
 1019  0
         this.persistenceService = persistenceService;
 1020  0
     }
 1021  
 
 1022  
     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
 1023  0
         this.persistenceStructureService = persistenceStructureService;
 1024  0
     }
 1025  
 
 1026  
     protected WorkflowAttributePropertyResolutionService getWorkflowAttributePropertyResolutionService() {
 1027  0
         if (workflowAttributePropertyResolutionService == null) {
 1028  0
             workflowAttributePropertyResolutionService =
 1029  
                     KRADServiceLocatorInternal.getWorkflowAttributePropertyResolutionService();
 1030  
         }
 1031  0
         return workflowAttributePropertyResolutionService;
 1032  
     }
 1033  
 
 1034  
     /**
 1035  
      * @return the collectionConstraintProcessors
 1036  
      */
 1037  
     @SuppressWarnings("unchecked")
 1038  
     public List<CollectionConstraintProcessor> getCollectionConstraintProcessors() {
 1039  0
         return this.collectionConstraintProcessors;
 1040  
     }
 1041  
 
 1042  
     /**
 1043  
      * @param collectionConstraintProcessors the collectionConstraintProcessors to set
 1044  
      */
 1045  
     @SuppressWarnings("unchecked")
 1046  
     public void setCollectionConstraintProcessors(List<CollectionConstraintProcessor> collectionConstraintProcessors) {
 1047  0
         this.collectionConstraintProcessors = collectionConstraintProcessors;
 1048  0
     }
 1049  
 
 1050  
     /**
 1051  
      * @return the constraintProviders
 1052  
      */
 1053  
     @SuppressWarnings("unchecked")
 1054  
     public List<ConstraintProvider> getConstraintProviders() {
 1055  0
         return this.constraintProviders;
 1056  
     }
 1057  
 
 1058  
     /**
 1059  
      * @param constraintProviders the constraintProviders to set
 1060  
      */
 1061  
     @SuppressWarnings("unchecked")
 1062  
     public void setConstraintProviders(List<ConstraintProvider> constraintProviders) {
 1063  0
         this.constraintProviders = constraintProviders;
 1064  0
     }
 1065  
 
 1066  
     /**
 1067  
      * @return the elementConstraintProcessors
 1068  
      */
 1069  
     @SuppressWarnings("unchecked")
 1070  
     public List<ConstraintProcessor> getElementConstraintProcessors() {
 1071  0
         return this.elementConstraintProcessors;
 1072  
     }
 1073  
 
 1074  
     /**
 1075  
      * @param elementConstraintProcessors the elementConstraintProcessors to set
 1076  
      */
 1077  
     @SuppressWarnings("unchecked")
 1078  
     public void setElementConstraintProcessors(List<ConstraintProcessor> elementConstraintProcessors) {
 1079  0
         this.elementConstraintProcessors = elementConstraintProcessors;
 1080  0
     }
 1081  
 
 1082  
     public DocumentDictionaryService getDocumentDictionaryService() {
 1083  0
         if (documentDictionaryService == null) {
 1084  0
             this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
 1085  
         }
 1086  0
         return documentDictionaryService;
 1087  
     }
 1088  
 
 1089  
     public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
 1090  0
         this.documentDictionaryService = documentDictionaryService;
 1091  0
     }
 1092  
 }