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