001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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: */
016: package org.kuali.core.service.impl;
017:
018: import java.beans.PropertyDescriptor;
019: import java.lang.reflect.InvocationTargetException;
020: import java.math.BigDecimal;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.regex.Pattern;
026:
027: import org.apache.commons.beanutils.PropertyUtils;
028: import org.apache.commons.lang.ArrayUtils;
029: import org.apache.commons.lang.StringUtils;
030: import org.kuali.RiceKeyConstants;
031: import org.kuali.core.bo.Parameter;
032: import org.kuali.core.bo.PersistableBusinessObject;
033: import org.kuali.core.datadictionary.ApcRuleDefinition;
034: import org.kuali.core.datadictionary.ReferenceDefinition;
035: import org.kuali.core.datadictionary.control.ControlDefinition;
036: import org.kuali.core.document.Document;
037: import org.kuali.core.exceptions.InfrastructureException;
038: import org.kuali.core.service.BusinessObjectService;
039: import org.kuali.core.service.DataDictionaryService;
040: import org.kuali.core.service.DictionaryValidationService;
041: import org.kuali.core.service.KualiConfigurationService;
042: import org.kuali.core.service.MaintenanceDocumentDictionaryService;
043: import org.kuali.core.service.PersistenceService;
044: import org.kuali.core.service.PersistenceStructureService;
045: import org.kuali.core.util.ErrorMap;
046: import org.kuali.core.util.GlobalVariables;
047: import org.kuali.core.util.ObjectUtils;
048: import org.kuali.core.util.TypeUtils;
049: import org.springframework.transaction.annotation.Transactional;
050:
051: /**
052: * Validates Documents, Business Objects, and Attributes against the data dictionary. Including min, max lengths, and validating
053: * expressions. This is the default, Kuali delivered implementation.
054: */
055: @Transactional
056: public class DictionaryValidationServiceImpl implements
057: DictionaryValidationService {
058: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
059: .getLogger(DictionaryValidationServiceImpl.class);
060:
061: private DataDictionaryService dataDictionaryService;
062: private BusinessObjectService businessObjectService;
063: private MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
064: private PersistenceService persistenceService;
065: private KualiConfigurationService configService;
066:
067: private PersistenceStructureService persistenceStructureService;
068:
069: /**
070: * @see org.kuali.core.service.DictionaryValidationService#validateDocument(org.kuali.core.document.Document)
071: */
072: public void validateDocument(Document document) {
073: String documentEntryName = document.getDocumentHeader()
074: .getWorkflowDocument().getDocumentType();
075:
076: // validate primitive values
077: validatePrimitivesFromDescriptors(documentEntryName, document,
078: PropertyUtils.getPropertyDescriptors(document
079: .getClass()), "", true);
080: }
081:
082: /**
083: * @see org.kuali.core.service.DictionaryValidationService#validateDocumentAttribute(org.kuali.core.document.Document,
084: * java.lang.String,java.lang.String)
085: */
086: public void validateDocumentAttribute(Document document,
087: String attributeName, String errorPrefix) {
088: String documentEntryName = document.getDocumentHeader()
089: .getWorkflowDocument().getDocumentType();
090:
091: try {
092: PropertyDescriptor attributeDescriptor = PropertyUtils
093: .getPropertyDescriptor(document, attributeName);
094: validatePrimitiveFromDescriptor(documentEntryName,
095: document, attributeDescriptor, errorPrefix, true);
096: } catch (NoSuchMethodException e) {
097: throw new InfrastructureException(
098: "unable to find propertyDescriptor for property '"
099: + attributeName + "'", e);
100: } catch (IllegalAccessException e) {
101: throw new InfrastructureException(
102: "unable to access propertyDescriptor for property '"
103: + attributeName + "'", e);
104: } catch (InvocationTargetException e) {
105: throw new InfrastructureException(
106: "unable to invoke methods for property '"
107: + attributeName + "'", e);
108: }
109: }
110:
111: /**
112: * @see org.kuali.core.service.DictionaryValidationService#validateDocumentRecursively
113: */
114: public void validateDocumentRecursively(Document document, int depth) {
115: // validate primitives of document
116: validateDocument(document);
117:
118: // call method to recursively find business objects and validate
119: validateBusinessObjectsFromDescriptors(document, PropertyUtils
120: .getPropertyDescriptors(document.getClass()), depth);
121: }
122:
123: public void validateDocumentAndUpdatableReferencesRecursively(
124: Document document, int maxDepth, boolean validateRequired) {
125: String documentEntryName = document.getDocumentHeader()
126: .getWorkflowDocument().getDocumentType();
127: // validate primitive values of the document
128: validatePrimitivesFromDescriptors(documentEntryName, document,
129: PropertyUtils.getPropertyDescriptors(document
130: .getClass()), "", validateRequired);
131:
132: if (maxDepth > 0) {
133: validateUpdatabableReferencesRecursively(document,
134: maxDepth - 1, validateRequired);
135: }
136: }
137:
138: private void validateUpdatabableReferencesRecursively(
139: PersistableBusinessObject businessObject, int maxDepth,
140: boolean validateRequired) {
141: if (ObjectUtils.isNull(businessObject)) {
142: return;
143: }
144: Map<String, Class> references = persistenceStructureService
145: .listReferenceObjectFields(businessObject);
146: for (String referenceName : references.keySet()) {
147: if (persistenceStructureService.isReferenceUpdatable(
148: businessObject.getClass(), referenceName)) {
149: Object referenceObj = ObjectUtils.getPropertyValue(
150: businessObject, referenceName);
151:
152: if (ObjectUtils.isNull(referenceObj)
153: || !(referenceObj instanceof PersistableBusinessObject)) {
154: continue;
155: }
156:
157: PersistableBusinessObject referenceBusinessObject = (PersistableBusinessObject) referenceObj;
158: GlobalVariables.getErrorMap().addToErrorPath(
159: referenceName);
160: validateBusinessObject(referenceBusinessObject,
161: validateRequired);
162: if (maxDepth > 0) {
163: validateUpdatabableReferencesRecursively(
164: referenceBusinessObject, maxDepth - 1,
165: validateRequired);
166: }
167: GlobalVariables.getErrorMap().removeFromErrorPath(
168: referenceName);
169:
170: }
171: }
172: Map<String, Class> collections = persistenceStructureService
173: .listCollectionObjectTypes(businessObject);
174: for (String collectionName : collections.keySet()) {
175: if (persistenceStructureService.isCollectionUpdatable(
176: businessObject.getClass(), collectionName)) {
177: Object listObj = ObjectUtils.getPropertyValue(
178: businessObject, collectionName);
179:
180: if (ObjectUtils.isNull(listObj)) {
181: continue;
182: }
183:
184: if (!(listObj instanceof List)) {
185: LOG
186: .error("The reference named "
187: + collectionName
188: + " of BO class "
189: + businessObject.getClass()
190: .getName()
191: + " should be of type java.util.List to be validated properly.");
192: continue;
193: }
194:
195: List list = (List) listObj;
196: for (int i = 0; i < list.size(); i++) {
197: if (ObjectUtils.isNotNull(list.get(i))
198: && list.get(i) instanceof PersistableBusinessObject) {
199: String errorPathAddition = collectionName + "["
200: + Integer.toString(i) + "]";
201: PersistableBusinessObject element = (PersistableBusinessObject) list
202: .get(i);
203:
204: GlobalVariables.getErrorMap().addToErrorPath(
205: errorPathAddition);
206: validateBusinessObject(element,
207: validateRequired);
208: if (maxDepth > 0) {
209: validateUpdatabableReferencesRecursively(
210: element, maxDepth - 1,
211: validateRequired);
212: }
213: GlobalVariables.getErrorMap()
214: .removeFromErrorPath(errorPathAddition);
215: }
216: }
217: }
218: }
219: }
220:
221: /**
222: * @see org.kuali.core.service.DictionaryValidationService#validateBusinessObject(org.kuali.core.bo.BusinessObject)
223: */
224: public void validateBusinessObject(
225: PersistableBusinessObject businessObject) {
226: validateBusinessObject(businessObject, true);
227: }
228:
229: /**
230: * @see org.kuali.core.service.DictionaryValidationService#validateBusinessObject(org.kuali.core.bo.BusinessObject,boolean)
231: */
232: public void validateBusinessObject(
233: PersistableBusinessObject businessObject,
234: boolean validateRequired) {
235: if (ObjectUtils.isNull(businessObject)) {
236: return;
237: }
238:
239: // validate the primitive attributes of the bo
240: validatePrimitivesFromDescriptors(businessObject.getClass()
241: .getName(), businessObject, PropertyUtils
242: .getPropertyDescriptors(businessObject.getClass()), "",
243: validateRequired);
244: }
245:
246: /**
247: * @see org.kuali.core.service.DictionaryValidationService#isBusinessObjectValid(org.kuali.core.bo.BusinessObject)
248: */
249: public boolean isBusinessObjectValid(
250: PersistableBusinessObject businessObject) {
251: return isBusinessObjectValid(businessObject, null);
252: }
253:
254: /**
255: * @see org.kuali.core.service.DictionaryValidationService#isBusinessObjectValid(org.kuali.core.bo.BusinessObject, String)
256: */
257: public boolean isBusinessObjectValid(
258: PersistableBusinessObject businessObject, String prefix) {
259: boolean retval = false;
260: final ErrorMap errorMap = GlobalVariables.getErrorMap();
261: int originalErrorCount = errorMap.getErrorCount();
262:
263: errorMap.addToErrorPath(prefix);
264: validateBusinessObject(businessObject);
265: errorMap.removeFromErrorPath(prefix);
266:
267: return errorMap.getErrorCount() == originalErrorCount;
268: }
269:
270: /**
271: * @param businessObject - business object to validate
272: */
273: public void validateBusinessObjectsRecursively(
274: PersistableBusinessObject businessObject, int depth) {
275: if (ObjectUtils.isNull(businessObject)) {
276: return;
277: }
278:
279: // validate primitives and any specific bo validation
280: validateBusinessObject(businessObject);
281:
282: // call method to recursively find business objects and validate
283: validateBusinessObjectsFromDescriptors(businessObject,
284: PropertyUtils.getPropertyDescriptors(businessObject
285: .getClass()), depth);
286: }
287:
288: /**
289: * @see org.kuali.core.service.DictionaryValidationService#validateAttributeFormat
290: */
291: public void validateAttributeFormat(String objectClassName,
292: String attributeName, String attributeValue, String errorKey) {
293: String longAttributeLabel = getDataDictionaryService()
294: .getAttributeLabel(objectClassName, attributeName);
295: String shortAttributeLabel = getDataDictionaryService()
296: .getAttributeShortLabel(objectClassName, attributeName);
297: String errorLabel = longAttributeLabel + " ("
298: + shortAttributeLabel + ")";
299:
300: LOG.debug("(bo, attributeName, attributeValue) = ("
301: + objectClassName + "," + attributeName + ","
302: + attributeValue + ")");
303:
304: if (StringUtils.isNotBlank(attributeValue)) {
305: Integer maxLength = getDataDictionaryService()
306: .getAttributeMaxLength(objectClassName,
307: attributeName);
308: if ((maxLength != null)
309: && (maxLength.intValue() < attributeValue.length())) {
310: GlobalVariables.getErrorMap()
311: .putError(
312: errorKey,
313: RiceKeyConstants.ERROR_MAX_LENGTH,
314: new String[] { errorLabel,
315: maxLength.toString() });
316: return;
317: }
318: Pattern validationExpression = getDataDictionaryService()
319: .getAttributeValidatingExpression(objectClassName,
320: attributeName);
321: if (validationExpression != null
322: && !validationExpression.pattern().equals(".*")) {
323: LOG
324: .debug("(bo, attributeName, validationExpression) = ("
325: + objectClassName
326: + ","
327: + attributeName
328: + "," + validationExpression + ")");
329:
330: if (!validationExpression.matcher(attributeValue)
331: .matches()) {
332: GlobalVariables
333: .getErrorMap()
334: .putError(
335: errorKey,
336: RiceKeyConstants.ERROR_INVALID_FORMAT,
337: new String[] { errorLabel,
338: attributeValue });
339: return;
340: }
341: }
342: BigDecimal exclusiveMin = getDataDictionaryService()
343: .getAttributeExclusiveMin(objectClassName,
344: attributeName);
345: if (exclusiveMin != null) {
346: try {
347: if (exclusiveMin.compareTo(new BigDecimal(
348: attributeValue)) >= 0) {
349: GlobalVariables.getErrorMap().putError(
350: errorKey,
351: RiceKeyConstants.ERROR_EXCLUSIVE_MIN,
352: // todo: Formatter for currency?
353: new String[] { errorLabel,
354: exclusiveMin.toString() });
355: return;
356: }
357: } catch (NumberFormatException e) {
358: // quash; this indicates that the DD contained a min for a non-numeric attribute
359: }
360: }
361: BigDecimal inclusiveMax = getDataDictionaryService()
362: .getAttributeInclusiveMax(objectClassName,
363: attributeName);
364: if (inclusiveMax != null) {
365: try {
366: if (inclusiveMax.compareTo(new BigDecimal(
367: attributeValue)) < 0) {
368: GlobalVariables.getErrorMap().putError(
369: errorKey,
370: RiceKeyConstants.ERROR_INCLUSIVE_MAX,
371: // todo: Formatter for currency?
372: new String[] { errorLabel,
373: inclusiveMax.toString() });
374: return;
375: }
376: } catch (NumberFormatException e) {
377: // quash; this indicates that the DD contained a max for a non-numeric attribute
378: }
379: }
380: }
381: }
382:
383: /**
384: * @see org.kuali.core.service.DictionaryValidationService#validateAttributeRequired
385: */
386: public void validateAttributeRequired(String objectClassName,
387: String attributeName, Object attributeValue,
388: Boolean forMaintenance, String errorKey) {
389: // check if field is a required field for the business object
390: if (attributeValue == null
391: || (attributeValue instanceof String && StringUtils
392: .isBlank((String) attributeValue))) {
393: Boolean required = getDataDictionaryService()
394: .isAttributeRequired(objectClassName, attributeName);
395: ControlDefinition controlDef = getDataDictionaryService()
396: .getAttributeControlDefinition(objectClassName,
397: attributeName);
398:
399: if (required != null && required.booleanValue()
400: && !(controlDef != null && controlDef.isHidden())) {
401:
402: // get label of attribute for message
403: String errorLabel = getDataDictionaryService()
404: .getAttributeErrorLabel(objectClassName,
405: attributeName);
406: GlobalVariables.getErrorMap().putError(errorKey,
407: RiceKeyConstants.ERROR_REQUIRED, errorLabel);
408: }
409: }
410: }
411:
412: /**
413: * iterates through the property discriptors looking for business objects or lists of business objects. calls validate method
414: * for each bo found
415: *
416: * @param object
417: * @param propertyDescriptors
418: */
419: private void validateBusinessObjectsFromDescriptors(Object object,
420: PropertyDescriptor[] propertyDescriptors, int depth) {
421: for (int i = 0; i < propertyDescriptors.length; i++) {
422: PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
423:
424: // validate the properties that are descended from BusinessObject
425: if (propertyDescriptor.getPropertyType() != null
426: && PersistableBusinessObject.class
427: .isAssignableFrom(propertyDescriptor
428: .getPropertyType())
429: && ObjectUtils.getPropertyValue(object,
430: propertyDescriptor.getName()) != null) {
431: PersistableBusinessObject bo = (PersistableBusinessObject) ObjectUtils
432: .getPropertyValue(object, propertyDescriptor
433: .getName());
434: GlobalVariables.getErrorMap().addToErrorPath(
435: propertyDescriptor.getName());
436: if (depth == 0) {
437: validateBusinessObject(bo);
438: } else {
439: validateBusinessObjectsRecursively(bo, depth - 1);
440: }
441: GlobalVariables.getErrorMap().removeFromErrorPath(
442: propertyDescriptor.getName());
443: }
444:
445: /*
446: * if property is a List, then walk the list and do the validation on each contained object that is a descendent of
447: * BusinessObject
448: */
449: else if (propertyDescriptor.getPropertyType() != null
450: && (List.class).isAssignableFrom(propertyDescriptor
451: .getPropertyType())
452: && ObjectUtils.getPropertyValue(object,
453: propertyDescriptor.getName()) != null) {
454: List propertyList = (List) ObjectUtils
455: .getPropertyValue(object, propertyDescriptor
456: .getName());
457: for (int j = 0; j < propertyList.size(); j++) {
458: if (propertyList.get(j) != null
459: && propertyList.get(j) instanceof PersistableBusinessObject) {
460: GlobalVariables.getErrorMap().addToErrorPath(
461: StringUtils.chomp(propertyDescriptor
462: .getName(), "s")
463: + "["
464: + (new Integer(j)).toString()
465: + "]");
466: if (depth == 0) {
467: validateBusinessObject((PersistableBusinessObject) propertyList
468: .get(j));
469: } else {
470: validateBusinessObjectsRecursively(
471: (PersistableBusinessObject) propertyList
472: .get(j), depth - 1);
473: }
474: GlobalVariables
475: .getErrorMap()
476: .removeFromErrorPath(
477: StringUtils
478: .chomp(
479: propertyDescriptor
480: .getName(),
481: "s")
482: + "["
483: + (new Integer(j))
484: .toString()
485: + "]");
486: }
487: }
488:
489: }
490: }
491: }
492:
493: /**
494: * iterates through property descriptors looking for primitives types, calls validate format and required check
495: *
496: * @param entryName
497: * @param object
498: * @param propertyDescriptors
499: * @param errorPrefix
500: */
501: private void validatePrimitivesFromDescriptors(String entryName,
502: Object object, PropertyDescriptor[] propertyDescriptors,
503: String errorPrefix, boolean validateRequired) {
504: for (int i = 0; i < propertyDescriptors.length; i++) {
505: PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
506:
507: validatePrimitiveFromDescriptor(entryName, object,
508: propertyDescriptor, errorPrefix, validateRequired);
509: }
510: }
511:
512: /**
513: * calls validate format and required check for the given propertyDescriptor
514: *
515: * @param entryName
516: * @param object
517: * @param propertyDescriptor
518: * @param errorPrefix
519: */
520: private void validatePrimitiveFromDescriptor(String entryName,
521: Object object, PropertyDescriptor propertyDescriptor,
522: String errorPrefix, boolean validateRequired) {
523: // validate the primitive attributes if defined in the dictionary
524: if (null != propertyDescriptor
525: && getDataDictionaryService().isAttributeDefined(
526: entryName, propertyDescriptor.getName())) {
527: Object value = ObjectUtils.getPropertyValue(object,
528: propertyDescriptor.getName());
529: Class propertyType = propertyDescriptor.getPropertyType();
530:
531: if (TypeUtils.isStringClass(propertyType)
532: || TypeUtils.isIntegralClass(propertyType)
533: || TypeUtils.isDecimalClass(propertyType)
534: || TypeUtils.isTemporalClass(propertyType)) {
535:
536: // check value format against dictionary
537: if (value != null
538: && StringUtils.isNotBlank(value.toString())) {
539: if (!TypeUtils.isTemporalClass(propertyType)) {
540: validateAttributeFormat(entryName,
541: propertyDescriptor.getName(), value
542: .toString(), errorPrefix
543: + propertyDescriptor.getName());
544: }
545: } else if (validateRequired) {
546: validateAttributeRequired(entryName,
547: propertyDescriptor.getName(), value,
548: Boolean.FALSE, errorPrefix
549: + propertyDescriptor.getName());
550: }
551: }
552: }
553: }
554:
555: /**
556: * @see org.kuali.core.service.DictionaryValidationService#validateReferenceExists(org.kuali.core.bo.BusinessObject,
557: * org.kuali.core.datadictionary.ReferenceDefinition)
558: */
559: public boolean validateReferenceExists(
560: PersistableBusinessObject bo, ReferenceDefinition reference) {
561: return validateReferenceExists(bo, reference.getAttributeName());
562: }
563:
564: /**
565: * @see org.kuali.core.service.DictionaryValidationService#validateReferenceExists(org.kuali.core.bo.BusinessObject,
566: * java.lang.String)
567: */
568: public boolean validateReferenceExists(
569: PersistableBusinessObject bo, String referenceName) {
570:
571: // attempt to retrieve the specified object from the db
572: PersistableBusinessObject referenceBo = businessObjectService
573: .getReferenceIfExists(bo, referenceName);
574:
575: // if it isnt there, then it doesnt exist, return false
576: if (ObjectUtils.isNotNull(referenceBo)) {
577: return true;
578: }
579:
580: // otherwise, it is there, return true
581: else {
582: return false;
583: }
584:
585: }
586:
587: /**
588: * @see org.kuali.core.service.DictionaryValidationService#validateReferenceIsActive(org.kuali.core.bo.BusinessObject,
589: * org.kuali.core.datadictionary.ReferenceDefinition)
590: */
591: public boolean validateReferenceIsActive(
592: PersistableBusinessObject bo, ReferenceDefinition reference) {
593: return validateReferenceIsActive(bo, reference
594: .getAttributeName(), reference
595: .getActiveIndicatorAttributeName(), reference
596: .isActiveIndicatorReversed());
597: }
598:
599: /**
600: * @see org.kuali.core.service.DictionaryValidationService#validateReferenceIsActive(org.kuali.core.bo.BusinessObject,
601: * java.lang.String, java.lang.String, boolean)
602: */
603: public boolean validateReferenceIsActive(
604: PersistableBusinessObject bo, String referenceName,
605: String activeIndicatorAttributeName,
606: boolean activeIndicatorReversed) {
607:
608: // attempt to retrieve the specified object from the db
609: PersistableBusinessObject referenceBo = businessObjectService
610: .getReferenceIfExists(bo, referenceName);
611:
612: // if the retrieved referenceBo is null, then we're done, return negative
613: if (referenceBo == null) {
614: return false;
615: }
616:
617: // We dont have to check that the field exists or is of type boolean here because
618: // the datadictionary validates this on load. If you're using this from anything other
619: // than a ReferenceDefinition loaded from the DataDictionary, you'll need to do some
620: // validation, and make sure the named attribute exists and is a boolean.
621:
622: // get the value in the field
623: Boolean activeIndicator;
624: try {
625: activeIndicator = (Boolean) PropertyUtils
626: .getSimpleProperty(referenceBo,
627: activeIndicatorAttributeName);
628: } catch (Exception e) {
629: throw new RuntimeException(e);
630: }
631:
632: // if we've ended up with a null
633: if (activeIndicator == null) {
634: throw new RuntimeException(
635: "Value of activeIndicator was Null. This should never happen.");
636: }
637:
638: if (activeIndicatorReversed) {
639: return !activeIndicator.booleanValue();
640: } else {
641: return activeIndicator.booleanValue();
642: }
643: }
644:
645: /**
646: * @see org.kuali.core.service.DictionaryValidationService#validateReferenceExistsAndIsActive(org.kuali.core.bo.BusinessObject,
647: * org.kuali.core.datadictionary.ReferenceDefinition)
648: */
649: public boolean validateReferenceExistsAndIsActive(
650: PersistableBusinessObject bo, ReferenceDefinition reference) {
651: boolean success = true;
652: // intelligently use the fieldname from the reference, or get it out
653: // of the dataDictionaryService
654: String displayFieldName;
655: if (reference.isDisplayFieldNameSet()) {
656: displayFieldName = reference.getDisplayFieldName();
657: } else {
658: Class boClass = reference.isCollectionReference() ? reference
659: .getCollectionBusinessObjectClass()
660: : bo.getClass();
661: displayFieldName = dataDictionaryService.getAttributeLabel(
662: boClass, reference.getAttributeToHighlightOnFail());
663: }
664:
665: if (reference.isCollectionReference()) {
666: success = validateCollectionReferenceExistsAndIsActive(bo,
667: reference, displayFieldName, StringUtils.split(
668: reference.getCollection(), "."), null);
669: } else {
670: success = validateReferenceExistsAndIsActive(bo, reference
671: .getAttributeName(), reference
672: .getActiveIndicatorAttributeName(), reference
673: .isActiveIndicatorReversed(), reference
674: .isActiveIndicatorSet(), reference
675: .getAttributeToHighlightOnFail(), displayFieldName);
676: }
677: return success;
678: }
679:
680: /**
681: * @param bo the object to get the collection from
682: * @param reference the <code>ReferenceDefinition</code> of the collection to validate
683: * @param displayFieldName the name of the field
684: * @param intermediateCollections array containing the path to the collection as tokens
685: * @param pathToAttribute the rebuilt path to the ReferenceDefinition.attributeToHighlightOnFail which includes the index of
686: * each subcollection
687: * @return
688: */
689: private boolean validateCollectionReferenceExistsAndIsActive(
690: PersistableBusinessObject bo,
691: ReferenceDefinition reference, String displayFieldName,
692: String[] intermediateCollections, String pathToAttributeI) {
693: boolean success = true;
694: Collection<PersistableBusinessObject> referenceCollection;
695: String collectionName = intermediateCollections[0];
696: // remove current collection from intermediates
697: intermediateCollections = (String[]) ArrayUtils.removeElement(
698: intermediateCollections, collectionName);
699: try {
700: referenceCollection = (Collection) PropertyUtils
701: .getProperty(bo, collectionName);
702: } catch (Exception e) {
703: throw new RuntimeException(e);
704: }
705: int pos = 0;
706: Iterator<PersistableBusinessObject> iterator = referenceCollection
707: .iterator();
708: while (iterator.hasNext()) {
709: String pathToAttribute = StringUtils
710: .defaultString(pathToAttributeI)
711: + collectionName + "[" + (pos++) + "].";
712: // keep drilling down until we reach the nested collection we want
713: if (intermediateCollections.length > 0) {
714: success &= validateCollectionReferenceExistsAndIsActive(
715: iterator.next(), reference, displayFieldName,
716: intermediateCollections, pathToAttribute);
717: } else {
718: String attributeToHighlightOnFail = pathToAttribute
719: + reference.getAttributeToHighlightOnFail();
720: success &= validateReferenceExistsAndIsActive(iterator
721: .next(), reference.getAttributeName(),
722: reference.getActiveIndicatorAttributeName(),
723: reference.isActiveIndicatorReversed(),
724: reference.isActiveIndicatorSet(),
725: attributeToHighlightOnFail, displayFieldName);
726: }
727: }
728:
729: return success;
730:
731: }
732:
733: /**
734: * @see org.kuali.core.service.DictionaryValidationService#validateReferenceExistsAndIsActive(org.kuali.core.bo.BusinessObject,
735: * java.lang.String, java.lang.String, boolean, boolean, java.lang.String, java.lang.String)
736: */
737: public boolean validateReferenceExistsAndIsActive(
738: PersistableBusinessObject bo, String referenceName,
739: String activeIndicatorAttributeName,
740: boolean activeIndicatorReversed,
741: boolean activeIndicatorSet,
742: String attributeToHighlightOnFail, String displayFieldName) {
743:
744: boolean success = true;
745: boolean exists;
746: boolean active;
747:
748: boolean fkFieldsPopulated = true;
749: // need to check for DD relationship FKs
750: List<String> fkFields = getDataDictionaryService()
751: .getRelationshipSourceAttributes(
752: bo.getClass().getName(), referenceName);
753: if (fkFields != null) {
754: for (String fkFieldName : fkFields) {
755: Object fkFieldValue = null;
756: try {
757: fkFieldValue = PropertyUtils.getProperty(bo,
758: fkFieldName);
759: }
760: // if we cant retrieve the field value, then
761: // it doesnt have a value
762: catch (IllegalAccessException e) {
763: fkFieldsPopulated = false;
764: } catch (InvocationTargetException e) {
765: fkFieldsPopulated = false;
766: } catch (NoSuchMethodException e) {
767: fkFieldsPopulated = false;
768: }
769:
770: // test the value
771: if (fkFieldValue == null) {
772: fkFieldsPopulated = false;
773: } else if (String.class.isAssignableFrom(fkFieldValue
774: .getClass())) {
775: if (StringUtils.isBlank((String) fkFieldValue)) {
776: fkFieldsPopulated = false;
777: }
778: }
779: }
780: } else { // if no DD relationship exists, check the persistence service
781: fkFieldsPopulated = persistenceService
782: .allForeignKeyValuesPopulatedForReference(bo,
783: referenceName);
784: }
785:
786: // only bother if all the fk fields have values
787: if (fkFieldsPopulated) {
788:
789: // do the existence test
790: exists = validateReferenceExists(bo, referenceName);
791: if (exists) {
792:
793: // do the active test, if appropriate
794: if (activeIndicatorSet) {
795: active = validateReferenceIsActive(bo,
796: referenceName,
797: activeIndicatorAttributeName,
798: activeIndicatorReversed);
799: if (!active) {
800: GlobalVariables.getErrorMap().putError(
801: attributeToHighlightOnFail,
802: RiceKeyConstants.ERROR_INACTIVE,
803: displayFieldName);
804: success &= false;
805: }
806: }
807: } else {
808: GlobalVariables.getErrorMap().putError(
809: attributeToHighlightOnFail,
810: RiceKeyConstants.ERROR_EXISTENCE,
811: displayFieldName);
812: success &= false;
813: }
814: }
815: return success;
816: }
817:
818: /**
819: * @see org.kuali.core.service.DictionaryValidationService#validateDefaultExistenceChecks(org.kuali.core.bo.BusinessObject)
820: */
821: public boolean validateDefaultExistenceChecks(
822: PersistableBusinessObject bo) {
823:
824: boolean success = true;
825:
826: // get a collection of all the referenceDefinitions setup for this object
827: Collection references = maintenanceDocumentDictionaryService
828: .getDefaultExistenceChecks(bo.getClass());
829:
830: // walk through the references, doing the tests on each
831: for (Iterator iter = references.iterator(); iter.hasNext();) {
832: ReferenceDefinition reference = (ReferenceDefinition) iter
833: .next();
834:
835: // do the existence and validation testing
836: success &= validateReferenceExistsAndIsActive(bo, reference);
837: }
838: return success;
839: }
840:
841: /**
842: * @see org.kuali.core.service.DictionaryValidationService#validateApcRule(org.kuali.core.bo.BusinessObject,
843: * org.kuali.core.datadictionary.ApcRuleDefinition)
844: */
845: public boolean validateApcRule(PersistableBusinessObject bo,
846: ApcRuleDefinition apcRule) {
847: boolean success = true;
848: Object attrValue;
849: try {
850: attrValue = PropertyUtils.getSimpleProperty(bo, apcRule
851: .getAttributeName());
852: // if the value we get back is null that means that the user didn't fill anything in
853: // so the rule shouldn't fail because of this
854: if (StringUtils.isEmpty((String) attrValue)) {
855: return success;
856: }
857: } catch (Exception e) {
858: throw new RuntimeException(e);
859: }
860:
861: String attrValueStr = attrValue.toString();
862: if (!configService.evaluateConstrainedValue(apcRule
863: .getParameterNamespace(), apcRule
864: .getParameterDetailType(), apcRule.getParameterName(),
865: attrValueStr)) {
866: success &= false;
867: GlobalVariables.getErrorMap().putError(
868: apcRule.getAttributeName(),
869: apcRule.getErrorMessage());
870: }
871:
872: return success;
873: }
874:
875: /**
876: * @see org.kuali.core.service.DictionaryValidationService#validateApcRules(org.kuali.core.bo.BusinessObject)
877: */
878: public boolean validateApcRules(PersistableBusinessObject bo) {
879: boolean success = true;
880:
881: // get a collection of all the apcRuleDefinitions setup for this object
882: Collection rules = maintenanceDocumentDictionaryService
883: .getApplyApcRules(bo.getClass());
884:
885: // walk through the rules, doing the tests on each
886: for (Iterator iter = rules.iterator(); iter.hasNext();) {
887: ApcRuleDefinition rule = (ApcRuleDefinition) iter.next();
888:
889: // do the existence and validation testing
890: success &= validateApcRule(bo, rule);
891: }
892: return success;
893: }
894:
895: /**
896: * @return Returns the dataDictionaryService.
897: */
898: public DataDictionaryService getDataDictionaryService() {
899: return dataDictionaryService;
900: }
901:
902: /**
903: * @param dataDictionaryService The dataDictionaryService to set.
904: */
905: public void setDataDictionaryService(
906: DataDictionaryService dataDictionaryService) {
907: this .dataDictionaryService = dataDictionaryService;
908: }
909:
910: /**
911: * Sets the businessObjectService attribute value.
912: *
913: * @param businessObjectService The businessObjectService to set.
914: */
915: public void setBusinessObjectService(
916: BusinessObjectService businessObjectService) {
917: this .businessObjectService = businessObjectService;
918: }
919:
920: /**
921: * Sets the maintenanceDocumentDictionaryService attribute value.
922: *
923: * @param maintenanceDocumentDictionaryService The maintenanceDocumentDictionaryService to set.
924: */
925: public void setMaintenanceDocumentDictionaryService(
926: MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService) {
927: this .maintenanceDocumentDictionaryService = maintenanceDocumentDictionaryService;
928: }
929:
930: /**
931: * Sets the persistenceService attribute value.
932: *
933: * @param persistenceService The persistenceService to set.
934: */
935: public void setPersistenceService(
936: PersistenceService persistenceService) {
937: this .persistenceService = persistenceService;
938: }
939:
940: /**
941: * Sets the configService attribute value
942: *
943: * @param configService the configService to set
944: */
945: public void setConfigService(KualiConfigurationService configService) {
946: this .configService = configService;
947: }
948:
949: public void setPersistenceStructureService(
950: PersistenceStructureService persistenceStructureService) {
951: this.persistenceStructureService = persistenceStructureService;
952: }
953:
954: }
|