001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.service.impl; 017 018import org.apache.commons.beanutils.PropertyUtils; 019import org.apache.commons.lang.ArrayUtils; 020import org.apache.commons.lang.StringUtils; 021import org.kuali.rice.core.api.mo.common.active.Inactivatable; 022import org.kuali.rice.core.api.util.RiceKeyConstants; 023import org.kuali.rice.krad.bo.BusinessObject; 024import org.kuali.rice.krad.bo.PersistableBusinessObject; 025import org.kuali.rice.krad.data.DataObjectWrapper; 026import org.kuali.rice.krad.data.KradDataServiceLocator; 027import org.kuali.rice.krad.datadictionary.CollectionDefinition; 028import org.kuali.rice.krad.datadictionary.ComplexAttributeDefinition; 029import org.kuali.rice.krad.datadictionary.DataDictionaryEntry; 030import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase; 031import org.kuali.rice.krad.datadictionary.DataObjectEntry; 032import org.kuali.rice.krad.datadictionary.ReferenceDefinition; 033import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException; 034import org.kuali.rice.krad.datadictionary.state.StateMapping; 035import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader; 036import org.kuali.rice.krad.datadictionary.validation.DictionaryObjectAttributeValueReader; 037import org.kuali.rice.krad.datadictionary.validation.ErrorLevel; 038import org.kuali.rice.krad.datadictionary.validation.capability.Constrainable; 039import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint; 040import org.kuali.rice.krad.datadictionary.validation.constraint.provider.ConstraintProvider; 041import org.kuali.rice.krad.datadictionary.validation.processor.CollectionConstraintProcessor; 042import org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor; 043import org.kuali.rice.krad.datadictionary.validation.result.ConstraintValidationResult; 044import org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult; 045import org.kuali.rice.krad.datadictionary.validation.result.ProcessorResult; 046import org.kuali.rice.krad.document.Document; 047import org.kuali.rice.krad.document.TransactionalDocument; 048import org.kuali.rice.krad.service.DataDictionaryService; 049import org.kuali.rice.krad.service.DictionaryValidationService; 050import org.kuali.rice.krad.service.DocumentDictionaryService; 051import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 052import org.kuali.rice.krad.service.LegacyDataAdapter; 053import org.kuali.rice.krad.uif.UifConstants; 054import org.kuali.rice.krad.uif.util.ConstraintStateUtils; 055import org.kuali.rice.krad.util.ErrorMessage; 056import org.kuali.rice.krad.util.GlobalVariables; 057import org.kuali.rice.krad.util.KRADUtils; 058import org.kuali.rice.krad.util.MessageMap; 059import org.springframework.beans.PropertyAccessorUtils; 060 061import java.beans.PropertyDescriptor; 062import java.lang.reflect.InvocationTargetException; 063import java.util.Collection; 064import java.util.IdentityHashMap; 065import java.util.Iterator; 066import java.util.LinkedList; 067import java.util.List; 068import java.util.Map; 069import java.util.Queue; 070import java.util.Set; 071 072/** 073 * Validates Documents, Business Objects, and Attributes against the data dictionary. Including min, max lengths, and 074 * validating expressions. This is the default, Kuali delivered implementation. 075 * 076 * KULRICE - 3355 Modified to prevent infinite looping (to maxDepth) scenario when a parent references a child which 077 * references a parent 078 * 079 * @author Kuali Rice Team (rice.collab@kuali.org) 080 */ 081public class DictionaryValidationServiceImpl implements DictionaryValidationService { 082 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger( 083 DictionaryValidationServiceImpl.class); 084 085 /** 086 * Constant defines a validation method for an attribute value. 087 * <p>Value is "validate" 088 */ 089 public static final String VALIDATE_METHOD = "validate"; 090 091 protected DataDictionaryService dataDictionaryService; 092 // protected BusinessObjectService businessObjectService; 093 // protected PersistenceService persistenceService; 094 protected DocumentDictionaryService documentDictionaryService; 095 // protected PersistenceStructureService persistenceStructureService; 096 @Deprecated 097 private LegacyDataAdapter legacyDataAdapter; 098 099 @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 protected final Set<Object> newIdentitySet() { 112 return java.util.Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()); 113 } 114 115 /** 116 * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object) 117 */ 118 @Override 119 public DictionaryValidationResult validate(Object object) { 120 return validate(object, object.getClass().getName(), (String) null, true); 121 } 122 123 /** 124 * @see org.kuali.rice.krad.service.DictionaryValidationService#validate(java.lang.Object, java.lang.String, 125 * java.lang.String, boolean) 126 */ 127 @Override 128 public DictionaryValidationResult validate(Object object, String entryName, String attributeName, 129 boolean doOptionalProcessing) { 130 StateMapping stateMapping = null; 131 String validationState = null; 132 DataDictionaryEntry entry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(entryName); 133 if (entry != null) { 134 stateMapping = entry.getStateMapping(); 135 if (stateMapping != null) { 136 validationState = stateMapping.getCurrentState(object); 137 } 138 } 139 140 AttributeValueReader attributeValueReader = new DictionaryObjectAttributeValueReader(object, entryName, entry); 141 attributeValueReader.setAttributeName(attributeName); 142 return validate(attributeValueReader, doOptionalProcessing, validationState, stateMapping); 143 } 144 145 /** 146 * @see DictionaryValidationService#validateAgainstNextState(Object) 147 */ 148 @Override 149 public DictionaryValidationResult validateAgainstNextState(Object object) { 150 String entryName = object.getClass().getName(); 151 StateMapping stateMapping = null; 152 String validationState = null; 153 DataDictionaryEntry entry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(entryName); 154 if (entry != null) { 155 stateMapping = entry.getStateMapping(); 156 if (stateMapping != null) { 157 validationState = stateMapping.getNextState(object); 158 } 159 } 160 AttributeValueReader attributeValueReader = new DictionaryObjectAttributeValueReader(object, entryName, entry); 161 return validate(attributeValueReader, true, validationState, stateMapping); 162 } 163 164 /** 165 * @see DictionaryValidationService#validateAgainstState(Object, String) 166 */ 167 @Override 168 public DictionaryValidationResult validateAgainstState(Object object, String validationState) { 169 String entryName = object.getClass().getName(); 170 StateMapping stateMapping = null; 171 DataDictionaryEntry entry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(entryName); 172 if (entry != null) { 173 stateMapping = entry.getStateMapping(); 174 if (stateMapping != null && StringUtils.isBlank(validationState)) { 175 validationState = stateMapping.getCurrentState(object); 176 } 177 } 178 179 AttributeValueReader attributeValueReader = new DictionaryObjectAttributeValueReader(object, entryName, entry); 180 return validate(attributeValueReader, true, validationState, stateMapping); 181 } 182 183 /** 184 * @see DictionaryValidationService#validate(Object, String, DataDictionaryEntry, boolean) 185 */ 186 @Override 187 public DictionaryValidationResult validate(Object object, String entryName, DataDictionaryEntry entry, 188 boolean doOptionalProcessing) { 189 StateMapping stateMapping = null; 190 String validationState = null; 191 if (entry != null) { 192 stateMapping = entry.getStateMapping(); 193 if (stateMapping != null) { 194 validationState = stateMapping.getCurrentState(object); 195 } 196 } 197 AttributeValueReader attributeValueReader = new DictionaryObjectAttributeValueReader(object, entryName, entry); 198 return validate(attributeValueReader, doOptionalProcessing, validationState, stateMapping); 199 } 200 201 /** 202 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDocument(org.kuali.rice.krad.document.Document) 203 */ 204 @Override 205 public void validateDocument(Document document) { 206 String documentEntryName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName(); 207 208 validate(document, documentEntryName, (String) null, true); 209 } 210 211 /** 212 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDocumentAttribute(org.kuali.rice.krad.document.Document, 213 * java.lang.String, java.lang.String) 214 */ 215 @Override 216 public void validateDocumentAttribute(Document document, String attributeName, String errorPrefix) { 217 String documentEntryName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName(); 218 219 validate(document, documentEntryName, attributeName, true); 220 } 221 222 /** 223 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDocumentAndUpdatableReferencesRecursively(org.kuali.rice.krad.document.Document, 224 * int, boolean) 225 */ 226 @Override 227 public void validateDocumentAndUpdatableReferencesRecursively(Document document, int maxDepth, 228 boolean validateRequired) { 229 validateDocumentAndUpdatableReferencesRecursively(document, maxDepth, validateRequired, false); 230 } 231 232 /** 233 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDocumentAndUpdatableReferencesRecursively(org.kuali.rice.krad.document.Document, 234 * int, boolean, boolean) 235 */ 236 @Override 237 public void validateDocumentAndUpdatableReferencesRecursively(Document document, int maxDepth, 238 boolean validateRequired, boolean chompLastLetterSFromCollectionName) { 239 String documentEntryName = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName(); 240 validate(document, documentEntryName, (String) null, true); 241 242 if (maxDepth > 0) { 243 validateUpdatabableReferencesRecursively(document, maxDepth - 1, validateRequired, 244 chompLastLetterSFromCollectionName, newIdentitySet()); 245 } 246 } 247 248 protected void validateUpdatabableReferencesRecursively(Object businessObject, int maxDepth, 249 boolean validateRequired, boolean chompLastLetterSFromCollectionName, Set<Object> processedBOs) { 250 // if null or already processed, return 251 if (KRADUtils.isNull(businessObject) || processedBOs.contains(businessObject)) { 252 return; 253 } 254 processedBOs.add(businessObject); // add bo to list to prevent excessive looping 255 Map<String, Class> references = getLegacyDataAdapter().listReferenceObjectFields(businessObject.getClass()); 256 for (String referenceName : references.keySet()) { 257 if (getLegacyDataAdapter().isReferenceUpdatable(businessObject.getClass(), referenceName)) { 258 Object referenceObj = KradDataServiceLocator.getDataObjectService().wrap(businessObject) 259 .getPropertyValueNullSafe(referenceName); 260 261 if (KRADUtils.isNull(referenceObj) || !(referenceObj instanceof PersistableBusinessObject)) { 262 continue; 263 } 264 265 BusinessObject referenceBusinessObject = (BusinessObject) referenceObj; 266 GlobalVariables.getMessageMap().addToErrorPath(referenceName); 267 validateBusinessObject(referenceBusinessObject, validateRequired); 268 if (maxDepth > 0) { 269 validateUpdatabableReferencesRecursively(referenceBusinessObject, maxDepth - 1, validateRequired, 270 chompLastLetterSFromCollectionName, processedBOs); 271 } 272 GlobalVariables.getMessageMap().removeFromErrorPath(referenceName); 273 } 274 } 275 Map<String, Class> collections = getLegacyDataAdapter().listCollectionObjectTypes(businessObject.getClass()); 276 for (String collectionName : collections.keySet()) { 277 if (getLegacyDataAdapter().isCollectionUpdatable(businessObject.getClass(), collectionName)) { 278 Object listObj = KradDataServiceLocator.getDataObjectService().wrap(businessObject) 279 .getPropertyValueNullSafe(collectionName); 280 281 if (KRADUtils.isNull(listObj)) { 282 continue; 283 } 284 285 if (!(listObj instanceof List)) { 286 if (LOG.isInfoEnabled()) { 287 LOG.info("The reference named " + collectionName + " of BO class " + 288 businessObject.getClass().getName() + 289 " should be of type java.util.List to be validated properly."); 290 } 291 continue; 292 } 293 294 List list = (List) listObj; 295 296 //should we materialize the proxied collection or just skip validation here assuming an unmaterialized objects are valid? 297 KRADUtils.materializeObjects(list); 298 299 for (int i = 0; i < list.size(); i++) { 300 final Object o = list.get(i); 301 if (KRADUtils.isNotNull(o) && o instanceof PersistableBusinessObject) { 302 final BusinessObject element = (BusinessObject) o; 303 304 final String errorPathAddition; 305 if (chompLastLetterSFromCollectionName) { 306 errorPathAddition = StringUtils.chomp(collectionName, "s") 307 + "[" 308 + Integer.toString(i) 309 + "]"; 310 } else { 311 errorPathAddition = collectionName + "[" + Integer.toString(i) + "]"; 312 } 313 314 GlobalVariables.getMessageMap().addToErrorPath(errorPathAddition); 315 validateBusinessObject(element, validateRequired); 316 if (maxDepth > 0) { 317 validateUpdatabableReferencesRecursively(element, maxDepth - 1, validateRequired, 318 chompLastLetterSFromCollectionName, processedBOs); 319 } 320 GlobalVariables.getMessageMap().removeFromErrorPath(errorPathAddition); 321 } 322 } 323 } 324 } 325 } 326 327 /** 328 * @see org.kuali.rice.krad.service.DictionaryValidationService#isBusinessObjectValid(org.kuali.rice.krad.bo.BusinessObject) 329 */ 330 @Override 331 public boolean isBusinessObjectValid(Object businessObject) { 332 return isBusinessObjectValid(businessObject, null); 333 } 334 335 /** 336 * @see org.kuali.rice.krad.service.DictionaryValidationService#isBusinessObjectValid(org.kuali.rice.krad.bo.BusinessObject, 337 * String) 338 */ 339 @Override 340 public boolean isBusinessObjectValid(Object businessObject, String prefix) { 341 final MessageMap errorMap = GlobalVariables.getMessageMap(); 342 int originalErrorCount = errorMap.getErrorCount(); 343 344 errorMap.addToErrorPath(prefix); 345 validateBusinessObject(businessObject); 346 errorMap.removeFromErrorPath(prefix); 347 348 return errorMap.getErrorCount() == originalErrorCount; 349 } 350 351 /** 352 * @param businessObject - business object to validate 353 */ 354 public void validateBusinessObjectsRecursively(Object businessObject, int depth) { 355 if (KRADUtils.isNull(businessObject)) { 356 return; 357 } 358 359 // validate primitives and any specific bo validation 360 validateBusinessObject(businessObject); 361 362 // call method to recursively find business objects and validate 363 validateBusinessObjectsFromDescriptors(businessObject, PropertyUtils.getPropertyDescriptors( 364 businessObject.getClass()), depth); 365 } 366 367 /** 368 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateBusinessObject(org.kuali.rice.krad.bo.BusinessObject) 369 */ 370 @Override 371 public void validateBusinessObject(Object businessObject) { 372 validateBusinessObject(businessObject, true); 373 } 374 375 /** 376 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateBusinessObject(org.kuali.rice.krad.bo.BusinessObject, 377 * boolean) 378 */ 379 @Override 380 public void validateBusinessObject(Object businessObject, boolean validateRequired) { 381 if (KRADUtils.isNull(businessObject)) { 382 return; 383 } 384 385 validate(businessObject, businessObject.getClass().getName(), (String) null, validateRequired); 386 } 387 388 /** 389 * iterates through the property descriptors looking for business objects or lists of business objects. calls 390 * validate method 391 * for each bo found 392 * 393 * @param object 394 * @param propertyDescriptors 395 */ 396 protected void validateBusinessObjectsFromDescriptors(Object object, PropertyDescriptor[] propertyDescriptors, 397 int depth) { 398 DataObjectWrapper<Object> wrapper = KradDataServiceLocator.getDataObjectService().wrap(object); 399 400 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { 401 // validate the properties that are descended from BusinessObject 402 if (propertyDescriptor.getPropertyType() != null && 403 PersistableBusinessObject.class.isAssignableFrom(propertyDescriptor.getPropertyType()) && 404 wrapper.getPropertyValueNullSafe(propertyDescriptor.getName()) != null) { 405 BusinessObject bo = (BusinessObject) wrapper.getPropertyValueNullSafe(propertyDescriptor.getName()); 406 if (depth == 0) { 407 GlobalVariables.getMessageMap().addToErrorPath(propertyDescriptor.getName()); 408 validateBusinessObject(bo); 409 GlobalVariables.getMessageMap().removeFromErrorPath(propertyDescriptor.getName()); 410 } else { 411 validateBusinessObjectsRecursively(bo, depth - 1); 412 } 413 } 414 415 /* 416 * if property is a List, then walk the list and do the validation on each contained object that is a descendent of 417 * BusinessObject 418 */ 419 else if (propertyDescriptor.getPropertyType() != null && 420 (List.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && 421 wrapper.getPropertyValueNullSafe(propertyDescriptor.getName()) != null) { 422 List propertyList = (List) wrapper.getPropertyValueNullSafe(propertyDescriptor.getName()); 423 for (int j = 0; j < propertyList.size(); j++) { 424 if (propertyList.get(j) != null && propertyList.get(j) instanceof PersistableBusinessObject) { 425 if (depth == 0) { 426 GlobalVariables.getMessageMap().addToErrorPath(StringUtils.chomp( 427 propertyDescriptor.getName(), "s") + "[" + 428 (new Integer(j)).toString() + "]"); 429 validateBusinessObject((BusinessObject) propertyList.get(j)); 430 GlobalVariables.getMessageMap().removeFromErrorPath(StringUtils.chomp( 431 propertyDescriptor.getName(), "s") + "[" + 432 (new Integer(j)).toString() + "]"); 433 } else { 434 validateBusinessObjectsRecursively((BusinessObject) propertyList.get(j), depth - 1); 435 } 436 } 437 } 438 } 439 } 440 } 441 442 /** 443 * calls validate format and required check for the given propertyDescriptor 444 * 445 * @param entryName 446 * @param object 447 * @param propertyDescriptor 448 * @param errorPrefix 449 * @deprecated since 1.1 450 */ 451 @Override 452 @Deprecated 453 public void validatePrimitiveFromDescriptor(String entryName, Object object, PropertyDescriptor propertyDescriptor, 454 String errorPrefix, boolean validateRequired) { 455 456 // validate the primitive attributes if defined in the dictionary 457 if (null != propertyDescriptor) { 458 validate(object, entryName, propertyDescriptor.getName(), validateRequired); 459 } 460 } 461 462 /** 463 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExists(java.lang.Object dataObject, 464 * org.kuali.rice.krad.datadictionary.ReferenceDefinition) 465 */ 466 @Override 467 public boolean validateReferenceExists(Object dataObject, ReferenceDefinition reference) { 468 return validateReferenceExists(dataObject, reference.getAttributeName()); 469 } 470 471 /** 472 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExists(java.lang.Object dataObject, 473 * java.lang.String) 474 */ 475 @Override 476 public boolean validateReferenceExists(Object dataObject, String referenceName) { 477 478 // attempt to retrieve the specified object from the db 479 Object referenceDataObject = getLegacyDataAdapter().getReferenceIfExists(dataObject, referenceName); 480 481 // if it isn't there, then it doesn't exist, return false 482 if (KRADUtils.isNotNull(referenceDataObject)) { 483 return true; 484 } 485 486 // otherwise, it is there, return true 487 return false; 488 } 489 490 /** 491 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceIsActive(java.lang.Object 492 * dataObject, 493 * org.kuali.rice.krad.datadictionary.ReferenceDefinition) 494 */ 495 @Override 496 public boolean validateReferenceIsActive(Object dataObject, ReferenceDefinition reference) { 497 return validateReferenceIsActive(dataObject, reference.getAttributeName()); 498 } 499 500 /** 501 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceIsActive(java.lang.Object 502 * dataObject, 503 * String) 504 */ 505 @Override 506 public boolean validateReferenceIsActive(Object dataObject, String referenceName) { 507 // attempt to retrieve the specified object from the db 508 Object referenceDataObject = getLegacyDataAdapter().getReferenceIfExists(dataObject, referenceName); 509 if (referenceDataObject == null) { 510 return false; 511 } 512 // mutable is related only to BusinessObject classes 513 if (!(referenceDataObject instanceof Inactivatable) || ((Inactivatable) referenceDataObject).isActive()) { 514 return true; 515 } 516 517 return false; 518 } 519 520 /** 521 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExistsAndIsActive(java.lang.Object 522 * dataObject, 523 * org.kuali.rice.krad.datadictionary.ReferenceDefinition) 524 */ 525 @Override 526 public boolean validateReferenceExistsAndIsActive(Object dataObject, ReferenceDefinition reference) { 527 boolean success = true; 528 // intelligently use the fieldname from the reference, or get it out 529 // of the dataDictionaryService 530 String displayFieldName; 531 if (reference.isDisplayFieldNameSet()) { 532 displayFieldName = reference.getDisplayFieldName(); 533 } else { 534 Class<?> boClass = reference.isCollectionReference() ? reference.getCollectionBusinessObjectClass() : 535 dataObject.getClass(); 536 displayFieldName = dataDictionaryService.getAttributeLabel(boClass, 537 reference.getAttributeToHighlightOnFail()); 538 } 539 540 if (reference.isCollectionReference()) { 541 success = validateCollectionReferenceExistsAndIsActive(dataObject, reference, displayFieldName, 542 StringUtils.split(reference.getCollection(), "."), null); 543 } else { 544 success = validateReferenceExistsAndIsActive(dataObject, reference.getAttributeName(), 545 reference.getAttributeToHighlightOnFail(), displayFieldName); 546 } 547 return success; 548 } 549 550 /** 551 * @param dataObject the object to get the collection from 552 * @param reference the <code>ReferenceDefinition</code> of the collection to validate 553 * @param displayFieldName the name of the field 554 * @param intermediateCollections array containing the path to the collection as tokens 555 * @param pathToAttributeI the rebuilt path to the ReferenceDefinition.attributeToHighlightOnFail which includes 556 * the 557 * index of 558 * each subcollection 559 * @return 560 */ 561 private boolean validateCollectionReferenceExistsAndIsActive(Object dataObject, ReferenceDefinition reference, 562 String displayFieldName, String[] intermediateCollections, String pathToAttributeI) { 563 boolean success = true; 564 Collection<?> referenceCollection; 565 String collectionName = intermediateCollections[0]; 566 // remove current collection from intermediates 567 intermediateCollections = (String[]) ArrayUtils.removeElement(intermediateCollections, collectionName); 568 try { 569 referenceCollection = (Collection) PropertyUtils.getProperty(dataObject, collectionName); 570 } catch (Exception e) { 571 throw new RuntimeException(e); 572 } 573 574 int pos = 0; 575 Iterator<?> iterator = referenceCollection.iterator(); 576 while (iterator.hasNext()) { 577 String pathToAttribute = StringUtils.defaultString(pathToAttributeI) 578 + collectionName + "[" + (pos++) + "]."; 579 // keep drilling down until we reach the nested collection we want 580 if (intermediateCollections.length > 0) { 581 success &= validateCollectionReferenceExistsAndIsActive(iterator.next(), reference, displayFieldName, 582 intermediateCollections, pathToAttribute); 583 } else { 584 String attributeToHighlightOnFail = pathToAttribute + reference.getAttributeToHighlightOnFail(); 585 success &= validateReferenceExistsAndIsActive(iterator.next(), reference.getAttributeName(), 586 attributeToHighlightOnFail, displayFieldName); 587 } 588 } 589 590 return success; 591 } 592 593 /** 594 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExistsAndIsActive(java.lang.Object 595 * dataObject, 596 * String, String, String) 597 */ 598 @Override 599 public boolean validateReferenceExistsAndIsActive(Object dataObject, String referenceName, 600 String attributeToHighlightOnFail, String displayFieldName) { 601 602 // if we're dealing with a nested attribute, we need to resolve down to the BO where the primitive attribute is located 603 // this is primarily to deal with the case of a defaultExistenceCheck that uses an "extension", i.e referenceName 604 // would be extension.attributeName 605 if (PropertyAccessorUtils.isNestedOrIndexedProperty(referenceName)) { 606 String nestedAttributePrefix = KRADUtils.getNestedAttributePrefix(referenceName); 607 String nestedAttributePrimitive = KRADUtils.getNestedAttributePrimitive(referenceName); 608 Object nestedObject = KradDataServiceLocator.getDataObjectService().wrap(dataObject) 609 .getPropertyValueNullSafe(nestedAttributePrefix); 610 return validateReferenceExistsAndIsActive(nestedObject, nestedAttributePrimitive, 611 attributeToHighlightOnFail, displayFieldName); 612 } 613 614 boolean hasReferences = validateFkFieldsPopulated(dataObject, referenceName); 615 boolean referenceExists = hasReferences && validateReferenceExists(dataObject, referenceName); 616 boolean canIncludeActiveReference = referenceExists && (!(dataObject instanceof Inactivatable) || 617 ((Inactivatable) dataObject).isActive()); 618 boolean referenceActive = canIncludeActiveReference && validateReferenceIsActive(dataObject, referenceName); 619 620 if(hasReferences && !referenceExists) { 621 GlobalVariables.getMessageMap().putError(attributeToHighlightOnFail, RiceKeyConstants.ERROR_EXISTENCE, 622 displayFieldName); 623 return false; 624 } else if(canIncludeActiveReference && !referenceActive) { 625 GlobalVariables.getMessageMap().putError(attributeToHighlightOnFail, RiceKeyConstants.ERROR_INACTIVE, 626 displayFieldName); 627 return false; 628 } 629 630 return true; 631 } 632 633 private boolean validateFkFieldsPopulated(Object dataObject, String referenceName) { 634 // need to check for DD relationship FKs 635 List<String> fkFields = getDataDictionaryService().getRelationshipSourceAttributes( 636 dataObject.getClass().getName(), referenceName); 637 if (fkFields != null) { 638 for (String fkFieldName : fkFields) { 639 Object fkFieldValue = null; 640 try { 641 fkFieldValue = PropertyUtils.getProperty(dataObject, fkFieldName); 642 } 643 // if we cant retrieve the field value, then 644 // it doesnt have a value 645 catch (IllegalAccessException e) { 646 return false; 647 } catch (InvocationTargetException e) { 648 return false; 649 } catch (NoSuchMethodException e) { 650 return false; 651 } 652 653 // test the value 654 if (fkFieldValue == null) { 655 return false; 656 } else if (String.class.isAssignableFrom(fkFieldValue.getClass())) { 657 if (StringUtils.isBlank((String) fkFieldValue)) { 658 return false; 659 } 660 } 661 } 662 } else { // if no DD relationship exists, check the persistence service / data object service 663 return getLegacyDataAdapter().allForeignKeyValuesPopulatedForReference(dataObject, referenceName); 664 } 665 return true; 666 } 667 668 /** 669 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecks(java.lang.Object 670 * dataObject) 671 */ 672 @Override 673 public boolean validateDefaultExistenceChecks(Object dataObject) { 674 boolean success = true; 675 676 // get a collection of all the referenceDefinitions setup for this object 677 Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(dataObject.getClass()); 678 679 // walk through the references, doing the tests on each 680 for (Iterator iter = references.iterator(); iter.hasNext(); ) { 681 ReferenceDefinition reference = (ReferenceDefinition) iter.next(); 682 683 // do the existence and validation testing 684 success &= validateReferenceExistsAndIsActive(dataObject, reference); 685 } 686 return success; 687 } 688 689 /** 690 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecksForNewCollectionItem(java.lang.Object 691 * dataObject, 692 * java.lang.Object newCollectionLine, java.lang.String collectionName) 693 */ 694 @Override 695 public boolean validateDefaultExistenceChecksForNewCollectionItem(Object dataObject, Object newCollectionItem, 696 String collectionName) { 697 boolean success = true; 698 699 if (StringUtils.isNotBlank(collectionName)) { 700 // get a collection of all the referenceDefinitions setup for this object 701 Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(dataObject.getClass()); 702 703 // walk through the references, doing the tests on each 704 for (Iterator iter = references.iterator(); iter.hasNext(); ) { 705 ReferenceDefinition reference = (ReferenceDefinition) iter.next(); 706 if (collectionName != null && collectionName.equals(reference.getCollection())) { 707 String displayFieldName; 708 if (reference.isDisplayFieldNameSet()) { 709 displayFieldName = reference.getDisplayFieldName(); 710 } else { 711 Class boClass = 712 reference.isCollectionReference() ? reference.getCollectionBusinessObjectClass() : 713 dataObject.getClass(); 714 displayFieldName = dataDictionaryService.getAttributeLabel(boClass, 715 reference.getAttributeToHighlightOnFail()); 716 } 717 718 success &= validateReferenceExistsAndIsActive(newCollectionItem, reference.getAttributeName(), 719 reference.getAttributeToHighlightOnFail(), displayFieldName); 720 } 721 } 722 } 723 724 return success; 725 } 726 727 /** 728 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecksForTransDoc(org.kuali.rice.krad.document.TransactionalDocument) 729 */ 730 @Override 731 public boolean validateDefaultExistenceChecksForTransDoc(TransactionalDocument document) { 732 boolean success = true; 733 734 // get a collection of all the referenceDefinitions setup for this object 735 Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(document); 736 737 // walk through the references, doing the tests on each 738 for (Iterator iter = references.iterator(); iter.hasNext(); ) { 739 ReferenceDefinition reference = (ReferenceDefinition) iter.next(); 740 741 // do the existence and validation testing 742 success &= validateReferenceExistsAndIsActive(document, reference); 743 } 744 return success; 745 } 746 747 /** 748 * @see org.kuali.rice.krad.service.DictionaryValidationService#validateDefaultExistenceChecksForNewCollectionItem(org.kuali.rice.krad.document.TransactionalDocument, 749 * org.kuali.rice.krad.bo.BusinessObject, String) 750 */ 751 @Override 752 public boolean validateDefaultExistenceChecksForNewCollectionItem(TransactionalDocument document, 753 Object newCollectionItem, String collectionName) { 754 boolean success = true; 755 if (StringUtils.isNotBlank(collectionName)) { 756 // get a collection of all the referenceDefinitions setup for this object 757 Collection references = getDocumentDictionaryService().getDefaultExistenceChecks(document); 758 759 // walk through the references, doing the tests on each 760 for (Iterator iter = references.iterator(); iter.hasNext(); ) { 761 ReferenceDefinition reference = (ReferenceDefinition) iter.next(); 762 if (collectionName != null && collectionName.equals(reference.getCollection())) { 763 String displayFieldName; 764 if (reference.isDisplayFieldNameSet()) { 765 displayFieldName = reference.getDisplayFieldName(); 766 } else { 767 Class boClass = 768 reference.isCollectionReference() ? reference.getCollectionBusinessObjectClass() : 769 document.getClass(); 770 displayFieldName = dataDictionaryService.getAttributeLabel(boClass, 771 reference.getAttributeToHighlightOnFail()); 772 } 773 774 success &= validateReferenceExistsAndIsActive(newCollectionItem, reference.getAttributeName(), 775 reference.getAttributeToHighlightOnFail(), displayFieldName); 776 } 777 } 778 } 779 return success; 780 } 781 782 /* 783 * 1.1 validation methods 784 */ 785 786 /** 787 * Validates using the defined AttributeValueReader (which allows access the object being validated) against 788 * the validationState and stateMapping (if specified). 789 * 790 * <p>If state information is null, validates the constraints as stateless (ie all constraints apply regardless of 791 * their states attribute).</p> 792 * 793 * @param valueReader - an object to validate 794 * @param doOptionalProcessing true if the validation should do optional validation (e.g. to check if empty values 795 * are required or not), false otherwise 796 * @param validationState 797 * @param stateMapping 798 * @return 799 */ 800 @Override 801 public DictionaryValidationResult validate(AttributeValueReader valueReader, boolean doOptionalProcessing, 802 String validationState, StateMapping stateMapping) { 803 DictionaryValidationResult result = new DictionaryValidationResult(); 804 805 if (valueReader.getAttributeName() == null) { 806 validateObject(result, valueReader, doOptionalProcessing, true, validationState, stateMapping); 807 } else { 808 validateAttribute(result, valueReader, doOptionalProcessing, validationState, stateMapping); 809 } 810 811 if (result.getNumberOfErrors() > 0) { 812 813 String[] prefixParams = new String[1]; 814 String prefixMessageKey = UifConstants.Messages.STATE_PREFIX; 815 if (stateMapping != null) { 816 prefixParams[0] = stateMapping.getStateNameMessage(validationState); 817 } 818 819 if (StringUtils.isBlank(prefixParams[0])) { 820 prefixMessageKey = null; 821 } 822 823 for (Iterator<ConstraintValidationResult> iterator = result.iterator(); iterator.hasNext(); ) { 824 ConstraintValidationResult constraintValidationResult = iterator.next(); 825 if (constraintValidationResult.getStatus().getLevel() >= ErrorLevel.WARN.getLevel()) { 826 String attributePath = constraintValidationResult.getAttributePath(); 827 if (attributePath == null || attributePath.isEmpty()) { 828 attributePath = constraintValidationResult.getAttributeName(); 829 } 830 831 if (constraintValidationResult.getConstraintLabelKey() != null) { 832 ErrorMessage errorMessage = new ErrorMessage(constraintValidationResult.getConstraintLabelKey(), 833 constraintValidationResult.getErrorParameters()); 834 errorMessage.setMessagePrefixKey(prefixMessageKey); 835 errorMessage.setMessagePrefixParameters(prefixParams); 836 GlobalVariables.getMessageMap().putError(attributePath, errorMessage); 837 } else { 838 ErrorMessage errorMessage = new ErrorMessage(constraintValidationResult.getErrorKey(), 839 constraintValidationResult.getErrorParameters()); 840 errorMessage.setMessagePrefixKey(prefixMessageKey); 841 errorMessage.setMessagePrefixParameters(prefixParams); 842 GlobalVariables.getMessageMap().putError(attributePath, errorMessage); 843 } 844 } 845 } 846 } 847 848 return result; 849 } 850 851 /** 852 * process constraints for the provided value using the element constraint processors 853 * 854 * @param result - used to store the validation results 855 * @param value - the object on which constraints are to be processed - the value of a complex attribute 856 * @param definition - a Data Dictionary definition e.g. {@code ComplexAttributeDefinition} 857 * @param attributeValueReader - a class that encapsulate access to both dictionary metadata and object field 858 * values 859 * @param doOptionalProcessing - true if the validation should do optional validation, false otherwise 860 */ 861 protected void processElementConstraints(DictionaryValidationResult result, Object value, Constrainable definition, 862 AttributeValueReader attributeValueReader, boolean doOptionalProcessing, String validationState, 863 StateMapping stateMapping) { 864 processConstraints(result, elementConstraintProcessors, value, definition, attributeValueReader, 865 doOptionalProcessing, validationState, stateMapping); 866 } 867 868 /** 869 * process constraints for the provided collection using the collection constraint processors 870 * 871 * @param result - used to store the validation results 872 * @param collection - the object on which constraints are to be processed - a collection 873 * @param definition - a Data Dictionary definition e.g. {@code CollectionDefinition} 874 * @param attributeValueReader - a class that encapsulate access to both dictionary metadata and object field 875 * values 876 * @param doOptionalProcessing - true if the validation should do optional validation, false otherwise 877 */ 878 protected void processCollectionConstraints(DictionaryValidationResult result, Collection<?> collection, 879 Constrainable definition, AttributeValueReader attributeValueReader, boolean doOptionalProcessing, 880 String validationState, StateMapping stateMapping) { 881 processConstraints(result, collectionConstraintProcessors, collection, definition, attributeValueReader, 882 doOptionalProcessing, validationState, stateMapping); 883 } 884 885 /** 886 * process constraints for the provided value using the provided constraint processors 887 * 888 * @param result - used to store the validation results 889 * @param value - the object on which constraints are to be processed - a collection or the value of an attribute 890 * @param definition - a Data Dictionary definition e.g. {@code ComplexAttributeDefinition} or {@code 891 * CollectionDefinition} 892 * @param attributeValueReader - a class that encapsulate access to both dictionary metadata and object field 893 * values 894 * @param doOptionalProcessing - true if the validation should do optional validation, false otherwise 895 */ 896 @SuppressWarnings("unchecked") 897 private void processConstraints(DictionaryValidationResult result, 898 List<? extends ConstraintProcessor> constraintProcessors, Object value, Constrainable definition, 899 AttributeValueReader attributeValueReader, boolean doOptionalProcessing, String validationState, 900 StateMapping stateMapping) { 901 //TODO: Implement custom validators 902 903 if (constraintProcessors != null) { 904 Constrainable selectedDefinition = definition; 905 AttributeValueReader selectedAttributeValueReader = attributeValueReader; 906 907 // First - take the constrainable definition and get its constraints 908 909 Queue<Constraint> constraintQueue = new LinkedList<Constraint>(); 910 911 // Using a for loop to iterate through constraint processors because ordering is important 912 for (ConstraintProcessor<Object, Constraint> processor : constraintProcessors) { 913 914 // Let the calling method opt out of any optional processing 915 if (!doOptionalProcessing && processor.isOptional()) { 916 result.addSkipped(attributeValueReader, processor.getName()); 917 continue; 918 } 919 920 Class<? extends Constraint> constraintType = processor.getConstraintType(); 921 922 // Add all of the constraints for this constraint type for all providers to the queue 923 for (ConstraintProvider constraintProvider : constraintProviders) { 924 if (constraintProvider.isSupported(selectedDefinition)) { 925 Collection<Constraint> constraintList = constraintProvider.getConstraints(selectedDefinition, 926 constraintType); 927 if (constraintList != null) { 928 constraintQueue.addAll(constraintList); 929 } 930 } 931 } 932 933 // If there are no constraints provided for this definition, then just skip it 934 if (constraintQueue.isEmpty()) { 935 result.addSkipped(attributeValueReader, processor.getName()); 936 continue; 937 } 938 939 Collection<Constraint> additionalConstraints = new LinkedList<Constraint>(); 940 941 // This loop is functionally identical to a for loop, but it has the advantage of letting us keep the queue around 942 // and populate it with any new constraints contributed by the processor 943 while (!constraintQueue.isEmpty()) { 944 945 Constraint constraint = constraintQueue.poll(); 946 947 // If this constraint is not one that this process handles, then skip and add to the queue for the next processor; 948 // obviously this would be redundant (we're only looking at constraints that this processor can process) except that 949 // the previous processor might have stuck a new constraint (or constraints) on the queue 950 if (!constraintType.isInstance(constraint)) { 951 result.addSkipped(attributeValueReader, processor.getName()); 952 additionalConstraints.add(constraint); 953 continue; 954 } 955 956 constraint = ConstraintStateUtils.getApplicableConstraint(constraint, validationState, 957 stateMapping); 958 959 if (constraint != null) { 960 ProcessorResult processorResult = processor.process(result, value, constraint, 961 selectedAttributeValueReader); 962 963 Collection<Constraint> processorResultContraints = processorResult.getConstraints(); 964 if (processorResultContraints != null && processorResultContraints.size() > 0) { 965 constraintQueue.addAll(processorResultContraints); 966 } 967 968 // Change the selected definition to whatever was returned from the processor 969 if (processorResult.isDefinitionProvided()) { 970 selectedDefinition = processorResult.getDefinition(); 971 } 972 // Change the selected attribute value reader to whatever was returned from the processor 973 if (processorResult.isAttributeValueReaderProvided()) { 974 selectedAttributeValueReader = processorResult.getAttributeValueReader(); 975 } 976 } 977 } 978 979 // After iterating through all the constraints for this processor, add the ones that werent consumed by this processor to the queue 980 constraintQueue.addAll(additionalConstraints); 981 } 982 } 983 } 984 985 /** 986 * validates an attribute 987 * 988 * @param result - used to store the validation results 989 * @param attributeValueReader - a class that encapsulate access to both dictionary metadata and object field 990 * values 991 * @param checkIfRequired - check if empty values are required or not 992 * @throws AttributeValidationException 993 */ 994 protected void validateAttribute(DictionaryValidationResult result, AttributeValueReader attributeValueReader, 995 boolean checkIfRequired, String validationState, 996 StateMapping stateMapping) throws AttributeValidationException { 997 Constrainable definition = attributeValueReader.getDefinition(attributeValueReader.getAttributeName()); 998 validateAttribute(result, definition, attributeValueReader, checkIfRequired, validationState, stateMapping); 999 } 1000 1001 /** 1002 * Validates the attribute specified by definition 1003 * 1004 * @param definition - the constrainable attribute definition of a specific attribute name 1005 * @throws AttributeValidationException 1006 */ 1007 protected void validateAttribute(DictionaryValidationResult result, Constrainable definition, 1008 AttributeValueReader attributeValueReader, boolean checkIfRequired, String validationState, 1009 StateMapping stateMapping) throws AttributeValidationException { 1010 1011 if (definition == null) { 1012 throw new AttributeValidationException( 1013 "Unable to validate constraints for attribute \"" + attributeValueReader.getAttributeName() + 1014 "\" on entry \"" + attributeValueReader.getEntryName() + 1015 "\" because no attribute definition can be found."); 1016 } 1017 1018 Object value = attributeValueReader.getValue(); 1019 1020 processElementConstraints(result, value, definition, attributeValueReader, checkIfRequired, validationState, 1021 stateMapping); 1022 } 1023 1024 /** 1025 * validates an object and its attributes recursively 1026 * 1027 * @param result - used to store the validation results 1028 * @param attributeValueReader - a class that encapsulate access to both dictionary metadata and object field 1029 * values 1030 * @param doOptionalProcessing - true if the validation should do optional validation, false otherwise 1031 * @param processAttributes - if true process all attribute definitions, skip if false 1032 * @throws AttributeValidationException 1033 */ 1034 protected void validateObject(DictionaryValidationResult result, AttributeValueReader attributeValueReader, 1035 boolean doOptionalProcessing, boolean processAttributes, String validationState, 1036 StateMapping stateMapping) throws AttributeValidationException { 1037 1038 // 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 1039 Constrainable objectEntry = attributeValueReader.getEntry(); 1040 processElementConstraints(result, attributeValueReader.getObject(), objectEntry, attributeValueReader, 1041 doOptionalProcessing, validationState, stateMapping); 1042 1043 List<Constrainable> definitions = attributeValueReader.getDefinitions(); 1044 1045 // Exit if the attribute value reader has no child definitions 1046 if (null == definitions) { 1047 return; 1048 } 1049 1050 //Process all attribute definitions (unless being skipped) 1051 if (processAttributes) { 1052 for (Constrainable definition : definitions) { 1053 String attributeName = definition.getName(); 1054 attributeValueReader.setAttributeName(attributeName); 1055 1056 if (attributeValueReader.isReadable()) { 1057 Object value = attributeValueReader.getValue(attributeName); 1058 1059 processElementConstraints(result, value, definition, attributeValueReader, doOptionalProcessing, 1060 validationState, stateMapping); 1061 } 1062 } 1063 } 1064 1065 //Process any constraints that may be defined on complex attributes 1066 if (objectEntry instanceof DataDictionaryEntryBase) { 1067 List<ComplexAttributeDefinition> complexAttrDefinitions = 1068 ((DataDictionaryEntryBase) objectEntry).getComplexAttributes(); 1069 1070 if (complexAttrDefinitions != null) { 1071 for (ComplexAttributeDefinition complexAttrDefinition : complexAttrDefinitions) { 1072 String attributeName = complexAttrDefinition.getName(); 1073 attributeValueReader.setAttributeName(attributeName); 1074 1075 if (attributeValueReader.isReadable()) { 1076 Object value = attributeValueReader.getValue(); 1077 1078 DataDictionaryEntry childEntry = complexAttrDefinition.getDataObjectEntry(); 1079 if (value != null) { 1080 AttributeValueReader nestedAttributeValueReader = new DictionaryObjectAttributeValueReader( 1081 value, childEntry.getFullClassName(), childEntry, attributeValueReader.getPath()); 1082 nestedAttributeValueReader.setAttributeName(attributeValueReader.getAttributeName()); 1083 //Validate nested object, however skip attribute definition porcessing on 1084 //nested object entry, since they have already been processed above. 1085 validateObject(result, nestedAttributeValueReader, doOptionalProcessing, false, 1086 validationState, stateMapping); 1087 } 1088 1089 processElementConstraints(result, value, complexAttrDefinition, attributeValueReader, 1090 doOptionalProcessing, validationState, stateMapping); 1091 } 1092 } 1093 } 1094 } 1095 1096 //FIXME: I think we may want to use a new CollectionConstrainable interface instead to obtain from 1097 //DictionaryObjectAttributeValueReader 1098 DataObjectEntry entry = (DataObjectEntry) attributeValueReader.getEntry(); 1099 if (entry != null) { 1100 for (CollectionDefinition collectionDefinition : entry.getCollections()) { 1101 //TODO: Do we need to be able to handle simple collections (ie. String, etc) 1102 1103 String childEntryName = collectionDefinition.getDataObjectClass(); 1104 String attributeName = collectionDefinition.getName(); 1105 attributeValueReader.setAttributeName(attributeName); 1106 1107 if (attributeValueReader.isReadable()) { 1108 Collection<?> collectionObject = attributeValueReader.getValue(); 1109 DataDictionaryEntry childEntry = childEntryName != null ? 1110 getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(childEntryName) : 1111 null; 1112 if (collectionObject != null) { 1113 int index = 0; 1114 for (Object value : collectionObject) { 1115 //NOTE: This path is only correct for collections that guarantee order 1116 String objectAttributePath = attributeValueReader.getPath() + "[" + index + "]"; 1117 1118 //FIXME: It's inefficient to be creating new attribute reader for each item in collection 1119 AttributeValueReader nestedAttributeValueReader = new DictionaryObjectAttributeValueReader( 1120 value, childEntryName, childEntry, objectAttributePath); 1121 validateObject(result, nestedAttributeValueReader, doOptionalProcessing, true, 1122 validationState, stateMapping); 1123 index++; 1124 } 1125 } 1126 1127 processCollectionConstraints(result, collectionObject, collectionDefinition, attributeValueReader, 1128 doOptionalProcessing, validationState, stateMapping); 1129 } 1130 } 1131 } 1132 } 1133 1134 /** 1135 * gets the {@link DataDictionaryService} 1136 * 1137 * @return Returns the dataDictionaryService 1138 */ 1139 public DataDictionaryService getDataDictionaryService() { 1140 return dataDictionaryService; 1141 } 1142 1143 /** 1144 * sets the {@link DataDictionaryService} 1145 * 1146 * @param dataDictionaryService The dataDictionaryService to set 1147 */ 1148 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1149 this.dataDictionaryService = dataDictionaryService; 1150 } 1151 1152 /** 1153 * gets the list of {@link CollectionConstraintProcessor} 1154 * 1155 * <p>Collection constraint processors are classes that determine if a feature of a collection of objects 1156 * satisfies some constraint</p> 1157 * 1158 * @return the collectionConstraintProcessors 1159 */ 1160 @SuppressWarnings("unchecked") 1161 public List<CollectionConstraintProcessor> getCollectionConstraintProcessors() { 1162 return this.collectionConstraintProcessors; 1163 } 1164 1165 /** 1166 * sets the list of {@link CollectionConstraintProcessor} 1167 * 1168 * @param collectionConstraintProcessors the collectionConstraintProcessors to set 1169 */ 1170 @SuppressWarnings("unchecked") 1171 public void setCollectionConstraintProcessors(List<CollectionConstraintProcessor> collectionConstraintProcessors) { 1172 this.collectionConstraintProcessors = collectionConstraintProcessors; 1173 } 1174 1175 /** 1176 * gets the list of {@link ConstraintProvider}s 1177 * 1178 * <p>Constraint providers are classes that map specific constraint types to a constraint resolver, 1179 * which takes a constrainable definition</p> 1180 * 1181 * @return the constraintProviders 1182 */ 1183 @SuppressWarnings("unchecked") 1184 public List<ConstraintProvider> getConstraintProviders() { 1185 return this.constraintProviders; 1186 } 1187 1188 /** 1189 * sets a list of {@link ConstraintProvider} 1190 * 1191 * @param constraintProviders the constraintProviders to set 1192 */ 1193 @SuppressWarnings("unchecked") 1194 public void setConstraintProviders(List<ConstraintProvider> constraintProviders) { 1195 this.constraintProviders = constraintProviders; 1196 } 1197 1198 /** 1199 * gets the list of element {@link ConstraintProcessor} 1200 * 1201 * <p>Element constraint processors are classes that determine if a passed value is valid 1202 * for a specific constraint at the individual object or object attribute level</p> 1203 * 1204 * @return the elementConstraintProcessors 1205 */ 1206 @SuppressWarnings("unchecked") 1207 public List<ConstraintProcessor> getElementConstraintProcessors() { 1208 return this.elementConstraintProcessors; 1209 } 1210 1211 /** 1212 * sets the list of {@link ConstraintProcessor} 1213 * 1214 * @param elementConstraintProcessors the elementConstraintProcessors to set 1215 */ 1216 @SuppressWarnings("unchecked") 1217 public void setElementConstraintProcessors(List<ConstraintProcessor> elementConstraintProcessors) { 1218 this.elementConstraintProcessors = elementConstraintProcessors; 1219 } 1220 1221 /** 1222 * gets the locally saved instance of @{link DocumentDictionaryService} 1223 * 1224 * <p>If the instance in this class has not be set, retrieve it using 1225 * {@link KRADServiceLocatorWeb#getDocumentDictionaryService()} and save locally</p> 1226 * 1227 * @return the locally saved instance of {@code DocumentDictionaryService} 1228 */ 1229 public DocumentDictionaryService getDocumentDictionaryService() { 1230 if (documentDictionaryService == null) { 1231 this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 1232 } 1233 return documentDictionaryService; 1234 } 1235 1236 /** 1237 * sets the {@link DocumentDictionaryService} 1238 * 1239 * @param documentDictionaryService - the {@code DocumentDictionaryService} to set 1240 */ 1241 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 1242 this.documentDictionaryService = documentDictionaryService; 1243 } 1244 1245 @Deprecated 1246 public LegacyDataAdapter getLegacyDataAdapter() { 1247 if (legacyDataAdapter == null) { 1248 legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter(); 1249 } 1250 return legacyDataAdapter; 1251 } 1252}