View Javadoc

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.kns.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Iterator;
21  import java.util.List;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.rice.kew.dto.DocumentTypeDTO;
25  import org.kuali.rice.kew.exception.WorkflowException;
26  import org.kuali.rice.kns.bo.PersistableBusinessObject;
27  import org.kuali.rice.kns.datadictionary.DataDictionary;
28  import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
29  import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
30  import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition;
31  import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition;
32  import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
33  import org.kuali.rice.kns.document.MaintenanceDocument;
34  import org.kuali.rice.kns.lookup.valueFinder.ValueFinder;
35  import org.kuali.rice.kns.maintenance.Maintainable;
36  import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
37  import org.kuali.rice.kns.service.DataDictionaryService;
38  import org.kuali.rice.kns.service.KNSServiceLocator;
39  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
40  import org.kuali.rice.kns.util.GlobalVariables;
41  import org.kuali.rice.kns.util.ObjectUtils;
42  import org.kuali.rice.kns.util.RiceKeyConstants;
43  
44  /**
45   * This class is the service implementation for the MaintenanceDocumentDictionary structure. Defines the API for the interacting
46   * with Document-related entries in the data dictionary. This is the default implementation, that is delivered with Kuali.
47   */
48  public class MaintenanceDocumentDictionaryServiceImpl implements MaintenanceDocumentDictionaryService {
49      protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintenanceDocumentDictionaryServiceImpl.class);
50  
51      private DataDictionaryService dataDictionaryService;
52  
53      /**
54       * This method gets the workflow document type for the given documentTypeName
55       * 
56       * @param documentTypeName
57       * @return
58       */
59      protected DocumentTypeDTO getDocumentType(String documentTypeName) {
60          try {
61              return KNSServiceLocator.getWorkflowInfoService().getDocType(documentTypeName);
62          } catch (WorkflowException e) {
63              throw new RuntimeException("Caught exception attempting to get document type for doc type name '" + documentTypeName + "'", e);
64          }
65      }
66  
67      /**
68       * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintenanceLabel(java.lang.String)
69       */
70      public String getMaintenanceLabel(String docTypeName) {
71          String label = null;
72  
73          DocumentTypeDTO docType = getDocumentType(docTypeName);
74          if (docType != null) {
75              label = docType.getDocTypeLabel();
76          }
77  
78          return label;
79      }
80  
81      /**
82       * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintenanceDescription(java.lang.String)
83       */
84      public String getMaintenanceDescription(String docTypeName) {
85          String description = null;
86  
87          DocumentTypeDTO docType = getDocumentType(docTypeName);
88          if (docType != null) {
89              description = docType.getDocTypeDescription();
90          }
91  
92          return description;
93      }
94  
95      /**
96       * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintainableClass(java.lang.String)
97       */
98      public Class getMaintainableClass(String docTypeName) {
99          Class maintainableClass = null;
100 
101         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
102         if (entry != null) {
103             LOG.debug("suppling a generic Rule to insure basic validation");
104             maintainableClass = entry.getMaintainableClass();
105         }
106 
107         return maintainableClass;
108     }
109 
110     /**
111      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getBusinessObjectClass(java.lang.String)
112      */
113     public Class getBusinessObjectClass(String docTypeName) {
114         Class businessObjectClass = null;
115 
116         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
117         if (entry != null) {
118             businessObjectClass = entry.getBusinessObjectClass();
119         }
120 
121         return businessObjectClass;
122     }
123 
124     /**
125      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getDocumentTypeName(java.lang.Class)
126      */
127     public String getDocumentTypeName(Class businessObjectClass) {
128         String documentTypeName = null;
129 
130         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(businessObjectClass);
131         if (entry != null) {
132             documentTypeName = entry.getDocumentTypeName();
133         }
134 
135         return documentTypeName;
136     }
137 
138     /**
139      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintainableSections(java.lang.String)
140      */
141     public List getMaintainableSections(String docTypeName) {
142         List sections = null;
143 
144         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
145         if (entry != null) {
146             sections = entry.getMaintainableSections();
147         }
148 
149         return sections;
150     }
151 
152     /**
153      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getBusinessRulesClass(MaintenanceDocument)
154      */
155     public Class getBusinessRulesClass(MaintenanceDocument document) {
156         Maintainable maintainable = document.getOldMaintainableObject();
157         if (maintainable == null) {
158             throw new IllegalArgumentException("unable to determine documentType for maintenanceDocument with no oldMaintainableObject");
159         }
160 
161         Class businessRulesClass = null;
162 
163         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(maintainable.getBoClass());
164         if (entry != null) {
165             businessRulesClass = entry.getBusinessRulesClass();
166         }
167 
168         if (businessRulesClass == null) {
169             return MaintenanceDocumentRuleBase.class; // default to a generic rule that will enforce Required fields
170         }
171 
172         LOG.info("return class: " + businessRulesClass.getName());
173 
174         return businessRulesClass;
175     }
176 
177     /**
178      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getDefaultExistenceChecks(java.lang.Class)
179      */
180     public Collection getDefaultExistenceChecks(Class businessObjectClass) {
181         return getDefaultExistenceChecks(getDocumentTypeName(businessObjectClass));
182     }
183 
184     /**
185      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getDefaultExistenceChecks(java.lang.String)
186      */
187     public Collection getDefaultExistenceChecks(String docTypeName) {
188 
189         Collection defaultExistenceChecks = null;
190 
191         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
192         if (entry != null) {
193             defaultExistenceChecks = entry.getDefaultExistenceChecks();
194         }
195 
196         return defaultExistenceChecks;
197     }
198 
199     /**
200      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getApplyApcRules(java.lang.Class)
201      */
202     public Collection getApplyApcRules(Class businessObjectClass) {
203         return getApplyApcRules(getDocumentTypeName(businessObjectClass));
204     }
205 
206     /**
207      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getApplyApcRules(java.lang.String)
208      */
209     public Collection getApplyApcRules(String docTypeName) {
210 
211         Collection apcRules = null;
212 
213         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
214         if (entry != null) {
215             apcRules = entry.getApcRules();
216         }
217 
218         return apcRules;
219     }
220 
221     /**
222      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getLockingKeys(java.lang.String)
223      */
224     public List getLockingKeys(String docTypeName) {
225         List lockingKeys = null;
226 
227         MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
228         if (entry != null) {
229             lockingKeys = entry.getLockingKeyFieldNames();
230         }
231 
232         return lockingKeys;
233     }
234 
235     /**
236      * @param dataDictionaryService
237      */
238     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
239         this.dataDictionaryService = dataDictionaryService;
240     }
241 
242     /**
243      * @return
244      */
245     public DataDictionary getDataDictionary() {
246         return this.dataDictionaryService.getDataDictionary();
247     }
248 
249     /**
250      * @param docTypeName
251      * @return
252      */
253     public MaintenanceDocumentEntry getMaintenanceDocumentEntry(String docTypeName) {
254         if (StringUtils.isBlank(docTypeName)) {
255             throw new IllegalArgumentException("invalid (blank) docTypeName");
256         }
257 
258         MaintenanceDocumentEntry entry = (MaintenanceDocumentEntry)getDataDictionary().getDocumentEntry(docTypeName);
259         return entry;
260     }
261 
262     private MaintenanceDocumentEntry getMaintenanceDocumentEntry(Class businessObjectClass) {
263         if (businessObjectClass == null) {
264             throw new IllegalArgumentException("invalid (blank) businessObjectClass");
265         }
266 
267         MaintenanceDocumentEntry entry = getDataDictionary().getMaintenanceDocumentEntryForBusinessObjectClass(businessObjectClass);
268         return entry;
269     }
270 
271     /**
272      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getFieldDefaultValue(java.lang.Class, java.lang.String)
273      */
274     public String getFieldDefaultValue(Class boClass, String fieldName) {
275 
276         // input parameter validation
277         if (boClass == null) {
278             throw new IllegalArgumentException("The boClass parameter value specified was " + "null.  A valid class representing the boClass must " + "be specified.");
279         }
280 
281         // call the twin
282         return getFieldDefaultValue(getDocumentTypeName(boClass), fieldName);
283     }
284 
285     /**
286      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getFieldDefaultValue(java.lang.String, java.lang.String)
287      */
288     public String getFieldDefaultValue(String docTypeName, String fieldName) {
289 
290         // input parameter validation
291         if (StringUtils.isBlank(docTypeName)) {
292             throw new IllegalArgumentException("The docTypeName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the docTypeName must " + "be specified.");
293         }
294         if (StringUtils.isBlank(fieldName)) {
295             throw new IllegalArgumentException("The fieldName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the fieldName must " + "be specified.");
296         }
297 
298         // walk through the sections
299         List sections = getMaintainableSections(docTypeName);
300         for (Iterator sectionIterator = sections.iterator(); sectionIterator.hasNext();) {
301             MaintainableSectionDefinition section = (MaintainableSectionDefinition) sectionIterator.next();
302 
303             // walk through the fields
304             Collection fields = section.getMaintainableItems();
305             String defaultValue = getFieldDefaultValue(fields, fieldName);
306             // need to keep trying sections until a match is found
307             if (defaultValue != null) {
308                 return defaultValue;
309             }
310         }
311         return null;
312     }
313 
314     private String getFieldDefaultValue(Collection maintainableFields, String fieldName) {
315         for (Iterator iterator = maintainableFields.iterator(); iterator.hasNext();) {
316             MaintainableItemDefinition item = (MaintainableItemDefinition) iterator.next();
317             // only check fields...skip subcollections
318             if (item instanceof MaintainableFieldDefinition) {
319 
320                 MaintainableFieldDefinition field = (MaintainableFieldDefinition) item;
321 
322                 // if the field name matches
323                 if (field.getName().endsWith(fieldName)) {
324 
325                     // preferentially take the raw default value
326                     if (StringUtils.isNotBlank(field.getDefaultValue())) {
327                         return field.getDefaultValue();
328                     }
329 
330                     // take the valuefinder
331                     else if (field.getDefaultValueFinderClass() != null) {
332 
333                         // attempt to get an instance of the defaultValueFinderClass
334                         ValueFinder valueFinder = null;
335                         try {
336                             valueFinder = (ValueFinder) field.getDefaultValueFinderClass().newInstance();
337                         }
338                         catch (Exception e) {
339                             LOG.info("Exception obtaining valueFinder for collection field default value", e);
340                             valueFinder = null;
341                         }
342 
343                         // get the value
344                         if (valueFinder != null) {
345                             return valueFinder.getValue();
346                         }
347                     }
348                     // if we found the field, but no default anything, then we're done
349                     else {
350                         return null;
351                     }
352                 }
353             }
354         }
355         return null;
356     }
357 
358     /**
359      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getCollectionFieldDefaultValue(java.lang.String,
360      *      java.lang.String, java.lang.String)
361      */
362     public String getCollectionFieldDefaultValue(String docTypeName, String collectionName, String fieldName) {
363         // input parameter validation
364         if (StringUtils.isBlank(docTypeName)) {
365             throw new IllegalArgumentException("The docTypeName parameter value specified was blank, whitespace, or null.  A valid string representing the docTypeName must be specified.");
366         }
367         if (StringUtils.isBlank(fieldName)) {
368             throw new IllegalArgumentException("The fieldName parameter value specified was blank, whitespace, or null.  A valid string representing the fieldName must be specified.");
369         }
370         if (StringUtils.isBlank(collectionName)) {
371             throw new IllegalArgumentException("The collectionName parameter value specified was null.  A valid string representing the collectionName must be specified.");
372         }
373 
374         MaintainableCollectionDefinition coll = getMaintainableCollection(docTypeName, collectionName);
375         if (coll != null) {
376             Collection collectionFields = coll.getMaintainableFields();
377             return getFieldDefaultValue(collectionFields, fieldName);
378         }
379         return null;
380     }
381 
382     /**
383      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsCopy(MaintenanceDocument)
384      */
385     public Boolean getAllowsCopy(MaintenanceDocument document) {
386         Boolean allowsCopy = Boolean.FALSE;
387         if (document != null && document.getNewMaintainableObject() != null) {
388             MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(document.getNewMaintainableObject().getBoClass());
389             if (entry != null) {
390                 allowsCopy = Boolean.valueOf(entry.getAllowsCopy());
391             }
392         }
393 
394         return allowsCopy;
395     }
396 
397     /**
398      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsNewOrCopy(java.lang.String)
399      */
400     public Boolean getAllowsNewOrCopy(String docTypeName) {
401         Boolean allowsNewOrCopy = Boolean.FALSE;
402 
403         if (docTypeName != null) {
404             MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
405             if (entry != null) {
406                 allowsNewOrCopy = Boolean.valueOf(entry.getAllowsNewOrCopy());
407             }
408         }
409 
410         return allowsNewOrCopy;
411     }
412 
413     public MaintainableItemDefinition getMaintainableItem(String docTypeName, String itemName) {
414         // input parameter validation
415         if (StringUtils.isBlank(docTypeName)) {
416             throw new IllegalArgumentException("The docTypeName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the docTypeName must " + "be specified.");
417         }
418         if (StringUtils.isBlank(itemName)) {
419             throw new IllegalArgumentException("The itemName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the itemName must " + "be specified.");
420         }
421 
422         // split name for subcollections
423         String[] subItems = {};
424         subItems = StringUtils.split(itemName, ".");
425 
426 
427         // walk through the sections
428         List sections = getMaintainableSections(docTypeName);
429         for (Iterator sectionIterator = sections.iterator(); sectionIterator.hasNext();) {
430             MaintainableSectionDefinition section = (MaintainableSectionDefinition) sectionIterator.next();
431 
432             // walk through the fields
433             Collection fields = section.getMaintainableItems();
434             for (Iterator fieldIterator = fields.iterator(); fieldIterator.hasNext();) {
435                 MaintainableItemDefinition item = (MaintainableItemDefinition) fieldIterator.next();
436 
437                 if (item.getName().equals(itemName)) {
438                     return item;
439                 }
440                 // if collection check to see if it has sub collections
441                 // for now this only allows 1 level (i.e. a.b) it should be expanded at some point
442                 if (item instanceof MaintainableCollectionDefinition) {
443                     MaintainableCollectionDefinition col = (MaintainableCollectionDefinition) item;
444                     if ((subItems.length > 1) && (StringUtils.equals(col.getName(), subItems[0]))) {
445                         for (Iterator<MaintainableCollectionDefinition> colIterator = col.getMaintainableCollections().iterator(); colIterator.hasNext();) {
446                             MaintainableCollectionDefinition subCol = (MaintainableCollectionDefinition) colIterator.next();
447                             if (subCol.getName().equals(subItems[1])) {
448                                 return subCol;
449                             }
450                         }
451                     }
452                 }
453             }
454         }
455         return null;
456     }
457 
458     public MaintainableFieldDefinition getMaintainableField(String docTypeName, String fieldName) {
459         MaintainableItemDefinition item = getMaintainableItem(docTypeName, fieldName);
460         if (item != null && item instanceof MaintainableFieldDefinition) {
461             return (MaintainableFieldDefinition) item;
462         }
463         return null;
464     }
465 
466     public MaintainableCollectionDefinition getMaintainableCollection(String docTypeName, String collectionName) {
467         // strip brackets as they are not needed to get to collection class
468         // Like the other subcollections changes this currently only supports one sub level
469         if (StringUtils.contains(collectionName, "[")) {
470             collectionName = StringUtils.substringBefore(collectionName, "[") + StringUtils.substringAfter(collectionName, "]");
471         }
472         MaintainableItemDefinition item = getMaintainableItem(docTypeName, collectionName);
473         if (item != null && item instanceof MaintainableCollectionDefinition) {
474             return (MaintainableCollectionDefinition) item;
475         }
476         return null;
477     }
478 
479     public Class getCollectionBusinessObjectClass(String docTypeName, String collectionName) {
480         MaintainableCollectionDefinition coll = getMaintainableCollection(docTypeName, collectionName);
481         if (coll != null) {
482             return coll.getBusinessObjectClass();
483         }
484         return null;
485     }
486 
487     public List<MaintainableCollectionDefinition> getMaintainableCollections(String docTypeName) {
488         ArrayList<MaintainableCollectionDefinition> collections = new ArrayList<MaintainableCollectionDefinition>();
489 
490         // walk through the sections
491         List sections = getMaintainableSections(docTypeName);
492         for (Iterator sectionIterator = sections.iterator(); sectionIterator.hasNext();) {
493             MaintainableSectionDefinition section = (MaintainableSectionDefinition) sectionIterator.next();
494 
495             // walk through the fields
496             Collection fields = section.getMaintainableItems();
497             for (Iterator fieldIterator = fields.iterator(); fieldIterator.hasNext();) {
498                 MaintainableItemDefinition item = (MaintainableItemDefinition) fieldIterator.next();
499 
500                 if (item instanceof MaintainableCollectionDefinition) {
501                     collections.add((MaintainableCollectionDefinition) item);
502                     // collections.addAll( getMaintainableCollections( (MaintainableCollectionDefinition)item ) );
503                 }
504             }
505         }
506 
507         return collections;
508     }
509 
510     public List<MaintainableCollectionDefinition> getMaintainableCollections(MaintainableCollectionDefinition parentCollection) {
511         ArrayList<MaintainableCollectionDefinition> collections = new ArrayList<MaintainableCollectionDefinition>();
512 
513         // walk through the sections
514         Collection<MaintainableCollectionDefinition> colls = parentCollection.getMaintainableCollections();
515         for (MaintainableCollectionDefinition coll : colls) {
516             collections.add(coll);
517             collections.addAll(getMaintainableCollections(coll));
518         }
519 
520         return collections;
521     }
522 
523     /**
524      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#validateMaintenanceRequiredFields(org.kuali.rice.kns.document.MaintenanceDocument)
525      */
526     public void validateMaintenanceRequiredFields(MaintenanceDocument document) {
527         Maintainable newMaintainableObject = document.getNewMaintainableObject();
528         if (newMaintainableObject == null) {
529             LOG.error("New maintainable is null");
530             throw new RuntimeException("New maintainable is null");
531         }
532 
533         List<MaintainableSectionDefinition> maintainableSectionDefinitions = getMaintainableSections(getDocumentTypeName(newMaintainableObject.getBoClass()));
534         for (MaintainableSectionDefinition maintainableSectionDefinition : maintainableSectionDefinitions) {
535             for (MaintainableItemDefinition maintainableItemDefinition : maintainableSectionDefinition.getMaintainableItems()) {
536                 // validate fields
537                 if (maintainableItemDefinition instanceof MaintainableFieldDefinition) {
538                     validateMaintainableFieldRequiredFields((MaintainableFieldDefinition) maintainableItemDefinition, newMaintainableObject.getBusinessObject(), maintainableItemDefinition.getName());
539                 }
540                 // validate collections
541                 else if (maintainableItemDefinition instanceof MaintainableCollectionDefinition) {
542                     validateMaintainableCollectionsRequiredFields(newMaintainableObject.getBusinessObject(), (MaintainableCollectionDefinition) maintainableItemDefinition);
543                 }
544             }
545         }
546     }
547 
548     /**
549      * generates error message if a field is marked as required but is not filled in
550      * 
551      * @param maintainableFieldDefinition
552      * @param businessObject
553      * @param fieldName
554      */
555     private void validateMaintainableFieldRequiredFields(MaintainableFieldDefinition maintainableFieldDefinition, PersistableBusinessObject businessObject, String fieldName) {
556 
557         if (StringUtils.isBlank(fieldName)) {
558             throw new IllegalArgumentException("invalid fieldName parameter.");
559         }
560         // if required check we have a value for this field
561         if (maintainableFieldDefinition.isRequired() && !maintainableFieldDefinition.isUnconditionallyReadOnly() ) {
562             try {
563                 Object obj = ObjectUtils.getNestedValue(businessObject, fieldName);
564 
565                 if (obj == null || StringUtils.isBlank(obj.toString())) {
566                     String attributeLabel = dataDictionaryService.getAttributeLabel(businessObject.getClass(), fieldName);
567                     String shortLabel = dataDictionaryService.getAttributeShortLabel(businessObject.getClass(), fieldName);
568                     GlobalVariables.getMessageMap().putError(fieldName, RiceKeyConstants.ERROR_REQUIRED, attributeLabel + " (" + shortLabel + ")" );
569                 } else if ( fieldName.endsWith(".principalName") ) {
570                     // special handling to catch when the principalName is not really a valid user
571                     // pull the Person object and test the entity ID.  If that's null, then this
572                     // is just a shell user instance and does not represent a true user
573                     // the main principalId property on the main object would be null at this point
574                     // but it is also unconditionally read only and not tested - checking that would
575                     // require checking the relationships and be more complex than we want to get here
576                     String personProperty = ObjectUtils.getNestedAttributePrefix(fieldName); 
577                     if ( StringUtils.isNotBlank(personProperty) ) {
578                         if ( StringUtils.isBlank( (String)ObjectUtils.getNestedValue(businessObject, personProperty+".entityId") ) ) {
579                             String attributeLabel = dataDictionaryService.getAttributeLabel(businessObject.getClass(), fieldName);
580                             GlobalVariables.getMessageMap().putError(fieldName, RiceKeyConstants.ERROR_EXISTENCE, attributeLabel );
581                         }
582                     }
583                 }
584             } catch( Exception ex ) {
585                 LOG.error( "unable to read property during doc required field checks", ex );
586             }
587         }
588     }
589 
590     
591     private MaintainableCollectionDefinition getCollectionDefinition( String docTypeName, String collectionName ) {
592         String currentCollection = collectionName;
593         String nestedCollections = "";
594     	if (StringUtils.contains(collectionName, "[")) {
595     		// strip off any array indexes
596             currentCollection = StringUtils.substringBefore( collectionName, "[" );
597             nestedCollections = StringUtils.substringAfter( collectionName, "." );
598     	}
599     	
600         // loop over all sections to find this collection
601         List<MaintainableSectionDefinition> maintainableSectionDefinitions = getMaintainableSections( docTypeName );
602         for (MaintainableSectionDefinition maintainableSectionDefinition : maintainableSectionDefinitions) {
603             for (MaintainableItemDefinition maintainableItemDefinition : maintainableSectionDefinition.getMaintainableItems()) {
604                 if (maintainableItemDefinition instanceof MaintainableCollectionDefinition && maintainableItemDefinition.getName().equals( currentCollection ) ) {
605                     if ( StringUtils.isBlank( nestedCollections ) ) {
606                         return (MaintainableCollectionDefinition) maintainableItemDefinition;
607                     } 
608                     
609                     return getCollectionDefinition( (MaintainableCollectionDefinition)maintainableItemDefinition, nestedCollections );
610                 }
611             }
612         }
613         
614         return null;
615     }
616 
617     private MaintainableCollectionDefinition getCollectionDefinition( MaintainableCollectionDefinition collectionDef, String collectionName ) {
618         String currentCollection = collectionName;
619         String nestedCollections = "";
620     	if (StringUtils.contains(collectionName, "[")) {
621     		// strip off any array indexes
622             currentCollection = StringUtils.substringBefore( collectionName, "[" );
623             nestedCollections = StringUtils.substringAfter( collectionName, "." );
624     	}
625         
626         // loop over all nested collections
627         for (MaintainableCollectionDefinition maintainableCollectionDefinition : collectionDef.getMaintainableCollections()) {
628             if ( maintainableCollectionDefinition.getName().equals( currentCollection ) ) {
629                 if ( StringUtils.isBlank( nestedCollections ) ) {
630                     return maintainableCollectionDefinition;
631                 } 
632                 return getCollectionDefinition( maintainableCollectionDefinition, nestedCollections );
633             }
634         }
635         
636         return null;
637     }
638     
639     public void validateMaintainableCollectionsAddLineRequiredFields(MaintenanceDocument document, PersistableBusinessObject businessObject, String collectionName ) {
640         MaintainableCollectionDefinition def = getCollectionDefinition( getDocumentTypeName(businessObject.getClass()), collectionName );
641         if ( def != null ) {
642             validateMaintainableCollectionsAddLineRequiredFields( document, businessObject, collectionName, def, 0);
643         }
644     }
645     /**
646      * calls code to generate error messages if maintainableFields within any collections or sub-collections are marked as required
647      * 
648      * @param document
649      * @param businessObject
650      * @param collectionName
651      * @param maintainableCollectionDefinition
652      * @param depth
653      */
654     private void validateMaintainableCollectionsAddLineRequiredFields(MaintenanceDocument document, PersistableBusinessObject businessObject, String collectionName, MaintainableCollectionDefinition maintainableCollectionDefinition, int depth) {
655         if ( depth == 0 ) {
656             GlobalVariables.getMessageMap().addToErrorPath("add");
657         }
658         // validate required fields on fields withing collection definition
659         PersistableBusinessObject element = document.getNewMaintainableObject().getNewCollectionLine( collectionName );
660         GlobalVariables.getMessageMap().addToErrorPath(collectionName);
661         for (MaintainableFieldDefinition maintainableFieldDefinition : maintainableCollectionDefinition.getMaintainableFields()) {
662             final String fieldName = maintainableFieldDefinition.getName();
663             validateMaintainableFieldRequiredFields(maintainableFieldDefinition, element, fieldName);
664             
665         }
666 
667         GlobalVariables.getMessageMap().removeFromErrorPath(collectionName);
668         if ( depth == 0 ) {
669             GlobalVariables.getMessageMap().removeFromErrorPath("add");
670         }
671     }
672 
673     /**
674      * calls code to generate error messages if maintainableFields within any collections or sub-collections are marked as required
675      * 
676      * @param businessObject
677      * @param maintainableCollectionDefinition
678      */
679     private void validateMaintainableCollectionsRequiredFields(PersistableBusinessObject businessObject, MaintainableCollectionDefinition maintainableCollectionDefinition) {
680         final String collectionName = maintainableCollectionDefinition.getName();
681 
682         // validate required fields on fields withing collection definition
683         Collection<PersistableBusinessObject> collection = (Collection) ObjectUtils.getPropertyValue(businessObject, collectionName);
684         if (collection != null && !collection.isEmpty()) {
685             for (MaintainableFieldDefinition maintainableFieldDefinition : maintainableCollectionDefinition.getMaintainableFields()) {
686                 int pos = 0;
687                 final String fieldName = maintainableFieldDefinition.getName();
688                 for (PersistableBusinessObject element : collection) {
689                     String parentName = collectionName + "[" + (pos++) + "]";
690                     GlobalVariables.getMessageMap().addToErrorPath(parentName);
691                     validateMaintainableFieldRequiredFields(maintainableFieldDefinition, element, fieldName);
692                     GlobalVariables.getMessageMap().removeFromErrorPath(parentName);
693                 }
694             }
695 
696             // recursivley validate required fields on subcollections
697             GlobalVariables.getMessageMap().addToErrorPath(collectionName);
698             for (MaintainableCollectionDefinition nestedMaintainableCollectionDefinition : maintainableCollectionDefinition.getMaintainableCollections()) {
699                 for (PersistableBusinessObject element : collection) {
700                     validateMaintainableCollectionsRequiredFields(element, nestedMaintainableCollectionDefinition);
701                 }
702             }
703             GlobalVariables.getMessageMap().removeFromErrorPath(collectionName);
704         }
705     }
706     
707     /**
708      * default implementation checks for duplicats based on keys of objects only
709      * 
710      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#validateMaintainableCollectionsForDuplicateEntries(org.kuali.rice.kns.document.MaintenanceDocument)
711      */
712     public void validateMaintainableCollectionsForDuplicateEntries(MaintenanceDocument document) {
713         Maintainable newMaintainableObject = document.getNewMaintainableObject();
714         if (newMaintainableObject == null) {
715             LOG.error("New maintainable is null");
716             throw new RuntimeException("New maintainable is null");
717         }
718 
719         List<MaintainableSectionDefinition> maintainableSectionDefinitions = getMaintainableSections(getDocumentTypeName(newMaintainableObject.getBoClass()));
720         for (MaintainableSectionDefinition maintainableSectionDefinition : maintainableSectionDefinitions) {
721             for (MaintainableItemDefinition maintainableItemDefinition : maintainableSectionDefinition.getMaintainableItems()) {
722                 // validate collections
723                 if (maintainableItemDefinition instanceof MaintainableCollectionDefinition) {
724                     validateMaintainableCollectionsForDuplicateEntries(newMaintainableObject.getBusinessObject(), (MaintainableCollectionDefinition) maintainableItemDefinition);
725                 }
726             }
727         }
728     }
729 
730     /**
731      * recursivly checks collections for duplicate entries based on key valuse
732      * 
733      * @param businessObject
734      * @param maintainableCollectionDefinition
735      */
736     private void validateMaintainableCollectionsForDuplicateEntries(PersistableBusinessObject businessObject, MaintainableCollectionDefinition maintainableCollectionDefinition) {
737         final String collectionName = maintainableCollectionDefinition.getName();
738 
739         if (maintainableCollectionDefinition.dissallowDuplicateKey()) {
740             final Class maintainableBusinessObjectClass = businessObject.getClass();
741             // validate that no duplicates based on keys exist
742             Collection<PersistableBusinessObject> collection = (Collection) ObjectUtils.getPropertyValue(businessObject, collectionName);
743             if (collection != null && !collection.isEmpty()) {
744                 final String propertyName = maintainableCollectionDefinition.getAttributeToHighlightOnDuplicateKey();
745                 // get collection label for dd
746                 final String label = dataDictionaryService.getCollectionLabel(maintainableBusinessObjectClass, collectionName);
747                 final String shortLabel = dataDictionaryService.getCollectionShortLabel(maintainableBusinessObjectClass, collectionName);
748                 int pos = 0;
749                 for (PersistableBusinessObject element : collection) {
750                     String pathToElement = collectionName + "[" + (pos++) + "]";
751                     if (ObjectUtils.countObjectsWithIdentitcalKey(collection, element) > 1) {
752                         GlobalVariables.getMessageMap().addToErrorPath(pathToElement);
753                         GlobalVariables.getMessageMap().putError(propertyName, RiceKeyConstants.ERROR_DUPLICATE_ELEMENT, new String[] { label, shortLabel });
754                         GlobalVariables.getMessageMap().removeFromErrorPath(pathToElement);
755                     }
756                 }
757 
758                 // recursivley check for duplicate entries on subcollections
759                 GlobalVariables.getMessageMap().addToErrorPath(collectionName);
760                 for (MaintainableCollectionDefinition nestedMaintainableCollectionDefinition : maintainableCollectionDefinition.getMaintainableCollections()) {
761                     for (PersistableBusinessObject element : collection) {
762                         validateMaintainableCollectionsForDuplicateEntries(element, nestedMaintainableCollectionDefinition);
763                     }
764                 }
765                 GlobalVariables.getMessageMap().removeFromErrorPath(collectionName);
766 
767             }
768         }
769     }
770        
771 	/**
772 	 * for issue KULRice 3072
773 	 * 
774 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getgetPreserveLockingKeysOnCopy(java.lang.Class)
775 	 */
776 	public boolean getPreserveLockingKeysOnCopy(Class businessObjectClass) {
777 
778 		boolean preserveLockingKeysOnCopy = false;
779 
780 		MaintenanceDocumentEntry docEntry = getMaintenanceDocumentEntry(businessObjectClass);
781 		
782 		if (docEntry != null) {
783 			preserveLockingKeysOnCopy = docEntry.getPreserveLockingKeysOnCopy();
784 		}
785 		
786 		return preserveLockingKeysOnCopy;
787 	}
788 
789 	/**
790 	 * for isue KULRice 3070
791 	 * 
792 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsRecordDeletion(java.lang.Class)
793 	 */
794 	public Boolean getAllowsRecordDeletion(Class businessObjectClass) {
795 		
796 		Boolean allowsRecordDeletion = Boolean.FALSE;
797 
798 		MaintenanceDocumentEntry docEntry = getMaintenanceDocumentEntry(businessObjectClass);
799 		
800 		if (docEntry != null) {
801 			allowsRecordDeletion = Boolean.valueOf(docEntry.getAllowsRecordDeletion());
802 		}
803 		
804 		return allowsRecordDeletion;
805 	}
806 
807 	/**
808 	 *  for issue KULRice3070, see if need delete button
809 	 * 
810 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsRecordDeletion(org.kuali.rice.kns.document.MaintenanceDocument)
811 	 */
812 	public Boolean getAllowsRecordDeletion(MaintenanceDocument document) {
813         return document != null ? this.getAllowsRecordDeletion(document.getNewMaintainableObject().getBoClass()) : Boolean.FALSE;
814 	}
815 
816 	/**
817 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#translateCodes(java.lang.Class)
818 	 */
819 	public Boolean translateCodes(Class businessObjectClass) {
820 		boolean translateCodes = false;
821 
822 		MaintenanceDocumentEntry docEntry = getMaintenanceDocumentEntry(businessObjectClass);
823 
824 		if (docEntry != null) {
825 			translateCodes = docEntry.isTranslateCodes();
826 		}
827 
828 		return translateCodes;
829 	}
830 
831 }