View Javadoc

1   /**
2    * Copyright 2005-2013 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.maintenance.rules.MaintenanceDocumentRuleBase;
30  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
31  import org.kuali.rice.krad.bo.PersistableBusinessObject;
32  import org.kuali.rice.krad.datadictionary.DataDictionary;
33  import org.kuali.rice.krad.rules.rule.BusinessRule;
34  import org.kuali.rice.krad.service.DataDictionaryService;
35  import org.kuali.rice.krad.util.GlobalVariables;
36  import org.kuali.rice.krad.util.ObjectUtils;
37  import org.kuali.rice.krad.valuefinder.ValueFinder;
38  
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.Iterator;
42  import java.util.List;
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  @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         MaintenanceDocumentEntry entry = (MaintenanceDocumentEntry) getDataDictionary().getMaintenanceDocumentEntryForBusinessObjectClass(businessObjectClass);
221         return entry;
222     }
223 
224     /**
225      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getFieldDefaultValue(java.lang.Class, java.lang.String)
226      */
227     public String getFieldDefaultValue(Class boClass, String fieldName) {
228 
229         // input parameter validation
230         if (boClass == null) {
231             throw new IllegalArgumentException("The boClass parameter value specified was " + "null.  A valid class representing the boClass must " + "be specified.");
232         }
233 
234         // call the twin
235         return getFieldDefaultValue(getDocumentTypeName(boClass), fieldName);
236     }
237 
238     /**
239      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getFieldDefaultValue(java.lang.String, java.lang.String)
240      */
241     public String getFieldDefaultValue(String docTypeName, String fieldName) {
242 
243         // input parameter validation
244         if (StringUtils.isBlank(docTypeName)) {
245             throw new IllegalArgumentException("The docTypeName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the docTypeName must " + "be specified.");
246         }
247         if (StringUtils.isBlank(fieldName)) {
248             throw new IllegalArgumentException("The fieldName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the fieldName must " + "be specified.");
249         }
250 
251         // walk through the sections
252         List sections = getMaintainableSections(docTypeName);
253         for (Iterator sectionIterator = sections.iterator(); sectionIterator.hasNext();) {
254             MaintainableSectionDefinition section = (MaintainableSectionDefinition) sectionIterator.next();
255 
256             // walk through the fields
257             Collection fields = section.getMaintainableItems();
258             String defaultValue = getFieldDefaultValue(fields, fieldName);
259             // need to keep trying sections until a match is found
260             if (defaultValue != null) {
261                 return defaultValue;
262             }
263         }
264         return null;
265     }
266 
267     private String getFieldDefaultValue(Collection maintainableFields, String fieldName) {
268         for (Iterator iterator = maintainableFields.iterator(); iterator.hasNext();) {
269             MaintainableItemDefinition item = (MaintainableItemDefinition) iterator.next();
270             // only check fields...skip subcollections
271             if (item instanceof MaintainableFieldDefinition) {
272 
273                 MaintainableFieldDefinition field = (MaintainableFieldDefinition) item;
274 
275                 // if the field name matches
276                 if (field.getName().endsWith(fieldName)) {
277 
278                     // preferentially take the raw default value
279                     if (StringUtils.isNotBlank(field.getDefaultValue())) {
280                         return field.getDefaultValue();
281                     }
282 
283                     // take the valuefinder
284                     else if (field.getDefaultValueFinderClass() != null) {
285 
286                         // attempt to get an instance of the defaultValueFinderClass
287                         ValueFinder valueFinder = null;
288                         try {
289                             valueFinder = (ValueFinder) field.getDefaultValueFinderClass().newInstance();
290                         }
291                         catch (Exception e) {
292                             LOG.info("Exception obtaining valueFinder for collection field default value", e);
293                             valueFinder = null;
294                         }
295 
296                         // get the value
297                         if (valueFinder != null) {
298                             return valueFinder.getValue();
299                         }
300                     }
301                     // if we found the field, but no default anything, then we're done
302                     else {
303                         return null;
304                     }
305                 }
306             }
307         }
308         return null;
309     }
310 
311     /**
312      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getCollectionFieldDefaultValue(java.lang.String,
313      *      java.lang.String, java.lang.String)
314      */
315     public String getCollectionFieldDefaultValue(String docTypeName, String collectionName, String fieldName) {
316         // input parameter validation
317         if (StringUtils.isBlank(docTypeName)) {
318             throw new IllegalArgumentException("The docTypeName parameter value specified was blank, whitespace, or null.  A valid string representing the docTypeName must be specified.");
319         }
320         if (StringUtils.isBlank(fieldName)) {
321             throw new IllegalArgumentException("The fieldName parameter value specified was blank, whitespace, or null.  A valid string representing the fieldName must be specified.");
322         }
323         if (StringUtils.isBlank(collectionName)) {
324             throw new IllegalArgumentException("The collectionName parameter value specified was null.  A valid string representing the collectionName must be specified.");
325         }
326 
327         MaintainableCollectionDefinition coll = getMaintainableCollection(docTypeName, collectionName);
328         if (coll != null) {
329             Collection collectionFields = coll.getMaintainableFields();
330             return getFieldDefaultValue(collectionFields, fieldName);
331         }
332         return null;
333     }
334 
335     /**
336      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsCopy(MaintenanceDocument)
337      */
338     public Boolean getAllowsCopy(MaintenanceDocument document) {
339         Boolean allowsCopy = Boolean.FALSE;
340         if (document != null && document.getNewMaintainableObject() != null) {
341             MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(document.getNewMaintainableObject().getBoClass());
342             if (entry != null) {
343                 allowsCopy = Boolean.valueOf(entry.getAllowsCopy());
344             }
345         }
346 
347         return allowsCopy;
348     }
349 
350     /**
351      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsNewOrCopy(java.lang.String)
352      */
353     public Boolean getAllowsNewOrCopy(String docTypeName) {
354         Boolean allowsNewOrCopy = Boolean.FALSE;
355 
356         if (docTypeName != null) {
357             MaintenanceDocumentEntry entry = getMaintenanceDocumentEntry(docTypeName);
358             if (entry != null) {
359                 allowsNewOrCopy = Boolean.valueOf(entry.getAllowsNewOrCopy());
360             }
361         }
362 
363         return allowsNewOrCopy;
364     }
365 
366     public MaintainableItemDefinition getMaintainableItem(String docTypeName, String itemName) {
367         // input parameter validation
368         if (StringUtils.isBlank(docTypeName)) {
369             throw new IllegalArgumentException("The docTypeName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the docTypeName must " + "be specified.");
370         }
371         if (StringUtils.isBlank(itemName)) {
372             throw new IllegalArgumentException("The itemName parameter value specified was  " + "blank, whitespace, or null.  A valid string representing the itemName must " + "be specified.");
373         }
374 
375         // split name for subcollections
376         String[] subItems = {};
377         subItems = StringUtils.split(itemName, ".");
378 
379 
380         // walk through the sections
381         List sections = getMaintainableSections(docTypeName);
382         for (Iterator sectionIterator = sections.iterator(); sectionIterator.hasNext();) {
383             MaintainableSectionDefinition section = (MaintainableSectionDefinition) sectionIterator.next();
384 
385             // walk through the fields
386             Collection fields = section.getMaintainableItems();
387             for (Iterator fieldIterator = fields.iterator(); fieldIterator.hasNext();) {
388                 MaintainableItemDefinition item = (MaintainableItemDefinition) fieldIterator.next();
389 
390                 if (item.getName().equals(itemName)) {
391                     return item;
392                 }
393                 // if collection check to see if it has sub collections
394                 // for now this only allows 1 level (i.e. a.b) it should be expanded at some point
395                 if (item instanceof MaintainableCollectionDefinition) {
396                     MaintainableCollectionDefinition col = (MaintainableCollectionDefinition) item;
397                     if ((subItems.length > 1) && (StringUtils.equals(col.getName(), subItems[0]))) {
398                         for (Iterator<MaintainableCollectionDefinition> colIterator = col.getMaintainableCollections().iterator(); colIterator.hasNext();) {
399                             MaintainableCollectionDefinition subCol = (MaintainableCollectionDefinition) colIterator.next();
400                             if (subCol.getName().equals(subItems[1])) {
401                                 return subCol;
402                             }
403                         }
404                     }
405                 }
406             }
407         }
408         return null;
409     }
410 
411     public MaintainableFieldDefinition getMaintainableField(String docTypeName, String fieldName) {
412         MaintainableItemDefinition item = getMaintainableItem(docTypeName, fieldName);
413         if (item != null && item instanceof MaintainableFieldDefinition) {
414             return (MaintainableFieldDefinition) item;
415         }
416         return null;
417     }
418 
419     public MaintainableCollectionDefinition getMaintainableCollection(String docTypeName, String collectionName) {
420         // strip brackets as they are not needed to get to collection class
421         // Like the other subcollections changes this currently only supports one sub level
422         if (StringUtils.contains(collectionName, "[")) {
423             collectionName = StringUtils.substringBefore(collectionName, "[") + StringUtils.substringAfter(collectionName, "]");
424         }
425         MaintainableItemDefinition item = getMaintainableItem(docTypeName, collectionName);
426         if (item != null && item instanceof MaintainableCollectionDefinition) {
427             return (MaintainableCollectionDefinition) item;
428         }
429         return null;
430     }
431 
432     public Class getCollectionBusinessObjectClass(String docTypeName, String collectionName) {
433         MaintainableCollectionDefinition coll = getMaintainableCollection(docTypeName, collectionName);
434         if (coll != null) {
435             return coll.getBusinessObjectClass();
436         }
437         return null;
438     }
439 
440     public List<MaintainableCollectionDefinition> getMaintainableCollections(String docTypeName) {
441         ArrayList<MaintainableCollectionDefinition> collections = new ArrayList<MaintainableCollectionDefinition>();
442 
443         // walk through the sections
444         List sections = getMaintainableSections(docTypeName);
445         for (Iterator sectionIterator = sections.iterator(); sectionIterator.hasNext();) {
446             MaintainableSectionDefinition section = (MaintainableSectionDefinition) sectionIterator.next();
447 
448             // walk through the fields
449             Collection fields = section.getMaintainableItems();
450             for (Iterator fieldIterator = fields.iterator(); fieldIterator.hasNext();) {
451                 MaintainableItemDefinition item = (MaintainableItemDefinition) fieldIterator.next();
452 
453                 if (item instanceof MaintainableCollectionDefinition) {
454                     collections.add((MaintainableCollectionDefinition) item);
455                     // collections.addAll( getMaintainableCollections( (MaintainableCollectionDefinition)item ) );
456                 }
457             }
458         }
459 
460         return collections;
461     }
462 
463     public List<MaintainableCollectionDefinition> getMaintainableCollections(MaintainableCollectionDefinition parentCollection) {
464         ArrayList<MaintainableCollectionDefinition> collections = new ArrayList<MaintainableCollectionDefinition>();
465 
466         // walk through the sections
467         Collection<MaintainableCollectionDefinition> colls = parentCollection.getMaintainableCollections();
468         for (MaintainableCollectionDefinition coll : colls) {
469             collections.add(coll);
470             collections.addAll(getMaintainableCollections(coll));
471         }
472 
473         return collections;
474     }
475 
476     /**
477      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#validateMaintenanceRequiredFields(org.kuali.rice.krad.maintenance.MaintenanceDocument)
478      */
479     public void validateMaintenanceRequiredFields(MaintenanceDocument document) {
480         Maintainable newMaintainableObject = document.getNewMaintainableObject();
481         if (newMaintainableObject == null) {
482             LOG.error("New maintainable is null");
483             throw new RuntimeException("New maintainable is null");
484         }
485 
486         List<MaintainableSectionDefinition> maintainableSectionDefinitions = getMaintainableSections(getDocumentTypeName(newMaintainableObject.getBoClass()));
487         for (MaintainableSectionDefinition maintainableSectionDefinition : maintainableSectionDefinitions) {
488             for (MaintainableItemDefinition maintainableItemDefinition : maintainableSectionDefinition.getMaintainableItems()) {
489                 // validate fields
490                 if (maintainableItemDefinition instanceof MaintainableFieldDefinition) {
491                     validateMaintainableFieldRequiredFields((MaintainableFieldDefinition) maintainableItemDefinition, newMaintainableObject.getBusinessObject(), maintainableItemDefinition.getName());
492                 }
493                 // validate collections
494                 else if (maintainableItemDefinition instanceof MaintainableCollectionDefinition) {
495                     validateMaintainableCollectionsRequiredFields(newMaintainableObject.getBusinessObject(), (MaintainableCollectionDefinition) maintainableItemDefinition);
496                 }
497             }
498         }
499     }
500 
501     /**
502      * generates error message if a field is marked as required but is not filled in
503      * 
504      * @param maintainableFieldDefinition
505      * @param businessObject
506      * @param fieldName
507      */
508     private void validateMaintainableFieldRequiredFields(MaintainableFieldDefinition maintainableFieldDefinition, PersistableBusinessObject businessObject, String fieldName) {
509 
510         if (StringUtils.isBlank(fieldName)) {
511             throw new IllegalArgumentException("invalid fieldName parameter.");
512         }
513         // if required check we have a value for this field
514         if (maintainableFieldDefinition.isRequired() && !maintainableFieldDefinition.isUnconditionallyReadOnly() ) {
515             try {
516                 Object obj = ObjectUtils.getNestedValue(businessObject, fieldName);
517 
518                 if (obj == null || StringUtils.isBlank(obj.toString())) {
519                     String attributeLabel = dataDictionaryService.getAttributeLabel(businessObject.getClass(), fieldName);
520                     String shortLabel = dataDictionaryService.getAttributeShortLabel(businessObject.getClass(), fieldName);
521                     GlobalVariables.getMessageMap().putError(fieldName, RiceKeyConstants.ERROR_REQUIRED, attributeLabel + " (" + shortLabel + ")" );
522                 } else if ( fieldName.endsWith(".principalName") ) {
523                     // special handling to catch when the principalName is not really a valid user
524                     // pull the Person object and test the entity ID.  If that's null, then this
525                     // is just a shell user instance and does not represent a true user
526                     // the main principalId property on the main object would be null at this point
527                     // but it is also unconditionally read only and not tested - checking that would
528                     // require checking the relationships and be more complex than we want to get here
529                     String personProperty = ObjectUtils.getNestedAttributePrefix(fieldName); 
530                     if ( StringUtils.isNotBlank(personProperty) ) {
531                         if ( StringUtils.isBlank( (String)ObjectUtils.getNestedValue(businessObject, personProperty+".entityId") ) ) {
532                             String attributeLabel = dataDictionaryService.getAttributeLabel(businessObject.getClass(), fieldName);
533                             GlobalVariables.getMessageMap().putError(fieldName, RiceKeyConstants.ERROR_EXISTENCE, attributeLabel );
534                         }
535                     }
536                 }
537             } catch( Exception ex ) {
538                 LOG.error( "unable to read property during doc required field checks", ex );
539             }
540         }
541     }
542 
543     
544     private MaintainableCollectionDefinition getCollectionDefinition( String docTypeName, String collectionName ) {
545         String currentCollection = collectionName;
546         String nestedCollections = "";
547     	if (StringUtils.contains(collectionName, "[")) {
548     		// strip off any array indexes
549             currentCollection = StringUtils.substringBefore( collectionName, "[" );
550             nestedCollections = StringUtils.substringAfter( collectionName, "." );
551     	}
552     	
553         // loop over all sections to find this collection
554         List<MaintainableSectionDefinition> maintainableSectionDefinitions = getMaintainableSections( docTypeName );
555         for (MaintainableSectionDefinition maintainableSectionDefinition : maintainableSectionDefinitions) {
556             for (MaintainableItemDefinition maintainableItemDefinition : maintainableSectionDefinition.getMaintainableItems()) {
557                 if (maintainableItemDefinition instanceof MaintainableCollectionDefinition && maintainableItemDefinition.getName().equals( currentCollection ) ) {
558                     if ( StringUtils.isBlank( nestedCollections ) ) {
559                         return (MaintainableCollectionDefinition) maintainableItemDefinition;
560                     } 
561                     
562                     return getCollectionDefinition( (MaintainableCollectionDefinition)maintainableItemDefinition, nestedCollections );
563                 }
564             }
565         }
566         
567         return null;
568     }
569 
570     private MaintainableCollectionDefinition getCollectionDefinition( MaintainableCollectionDefinition collectionDef, String collectionName ) {
571         String currentCollection = collectionName;
572         String nestedCollections = "";
573     	if (StringUtils.contains(collectionName, "[")) {
574     		// strip off any array indexes
575             currentCollection = StringUtils.substringBefore( collectionName, "[" );
576             nestedCollections = StringUtils.substringAfter( collectionName, "." );
577     	}
578         
579         // loop over all nested collections
580         for (MaintainableCollectionDefinition maintainableCollectionDefinition : collectionDef.getMaintainableCollections()) {
581             if ( maintainableCollectionDefinition.getName().equals( currentCollection ) ) {
582                 if ( StringUtils.isBlank( nestedCollections ) ) {
583                     return maintainableCollectionDefinition;
584                 } 
585                 return getCollectionDefinition( maintainableCollectionDefinition, nestedCollections );
586             }
587         }
588         
589         return null;
590     }
591     
592     public void validateMaintainableCollectionsAddLineRequiredFields(MaintenanceDocument document, PersistableBusinessObject businessObject, String collectionName ) {
593         MaintainableCollectionDefinition def = getCollectionDefinition( getDocumentTypeName(businessObject.getClass()), collectionName );
594         if ( def != null ) {
595             validateMaintainableCollectionsAddLineRequiredFields( document, businessObject, collectionName, def, 0);
596         }
597     }
598     /**
599      * calls code to generate error messages if maintainableFields within any collections or sub-collections are marked as required
600      * 
601      * @param document
602      * @param businessObject
603      * @param collectionName
604      * @param maintainableCollectionDefinition
605      * @param depth
606      */
607     private void validateMaintainableCollectionsAddLineRequiredFields(MaintenanceDocument document, PersistableBusinessObject businessObject, String collectionName, MaintainableCollectionDefinition maintainableCollectionDefinition, int depth) {
608         if ( depth == 0 ) {
609             GlobalVariables.getMessageMap().addToErrorPath("add");
610         }
611         // validate required fields on fields withing collection definition
612         PersistableBusinessObject element = document.getNewMaintainableObject().getNewCollectionLine( collectionName );
613         GlobalVariables.getMessageMap().addToErrorPath(collectionName);
614         for (MaintainableFieldDefinition maintainableFieldDefinition : maintainableCollectionDefinition.getMaintainableFields()) {
615             final String fieldName = maintainableFieldDefinition.getName();
616             validateMaintainableFieldRequiredFields(maintainableFieldDefinition, element, fieldName);
617             
618         }
619 
620         GlobalVariables.getMessageMap().removeFromErrorPath(collectionName);
621         if ( depth == 0 ) {
622             GlobalVariables.getMessageMap().removeFromErrorPath("add");
623         }
624     }
625 
626     /**
627      * calls code to generate error messages if maintainableFields within any collections or sub-collections are marked as required
628      * 
629      * @param businessObject
630      * @param maintainableCollectionDefinition
631      */
632     private void validateMaintainableCollectionsRequiredFields(PersistableBusinessObject businessObject, MaintainableCollectionDefinition maintainableCollectionDefinition) {
633         final String collectionName = maintainableCollectionDefinition.getName();
634 
635         // validate required fields on fields withing collection definition
636         Collection<PersistableBusinessObject> collection = (Collection) ObjectUtils.getPropertyValue(businessObject, collectionName);
637         if (collection != null && !collection.isEmpty()) {
638             for (MaintainableFieldDefinition maintainableFieldDefinition : maintainableCollectionDefinition.getMaintainableFields()) {
639                 int pos = 0;
640                 final String fieldName = maintainableFieldDefinition.getName();
641                 for (PersistableBusinessObject element : collection) {
642                     String parentName = collectionName + "[" + (pos++) + "]";
643                     GlobalVariables.getMessageMap().addToErrorPath(parentName);
644                     validateMaintainableFieldRequiredFields(maintainableFieldDefinition, element, fieldName);
645                     GlobalVariables.getMessageMap().removeFromErrorPath(parentName);
646                 }
647             }
648 
649             // recursivley validate required fields on subcollections
650             GlobalVariables.getMessageMap().addToErrorPath(collectionName);
651             for (MaintainableCollectionDefinition nestedMaintainableCollectionDefinition : maintainableCollectionDefinition.getMaintainableCollections()) {
652                 for (PersistableBusinessObject element : collection) {
653                     validateMaintainableCollectionsRequiredFields(element, nestedMaintainableCollectionDefinition);
654                 }
655             }
656             GlobalVariables.getMessageMap().removeFromErrorPath(collectionName);
657         }
658     }
659     
660     /**
661      * default implementation checks for duplicats based on keys of objects only
662      * 
663      * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#validateMaintainableCollectionsForDuplicateEntries(org.kuali.rice.krad.maintenance.MaintenanceDocument)
664      */
665     public void validateMaintainableCollectionsForDuplicateEntries(MaintenanceDocument document) {
666         Maintainable newMaintainableObject = document.getNewMaintainableObject();
667         if (newMaintainableObject == null) {
668             LOG.error("New maintainable is null");
669             throw new RuntimeException("New maintainable is null");
670         }
671 
672         List<MaintainableSectionDefinition> maintainableSectionDefinitions = getMaintainableSections(getDocumentTypeName(newMaintainableObject.getBoClass()));
673         for (MaintainableSectionDefinition maintainableSectionDefinition : maintainableSectionDefinitions) {
674             for (MaintainableItemDefinition maintainableItemDefinition : maintainableSectionDefinition.getMaintainableItems()) {
675                 // validate collections
676                 if (maintainableItemDefinition instanceof MaintainableCollectionDefinition) {
677                     validateMaintainableCollectionsForDuplicateEntries(newMaintainableObject.getBusinessObject(), (MaintainableCollectionDefinition) maintainableItemDefinition);
678                 }
679             }
680         }
681     }
682 
683     /**
684      * recursivly checks collections for duplicate entries based on key valuse
685      * 
686      * @param businessObject
687      * @param maintainableCollectionDefinition
688      */
689     private void validateMaintainableCollectionsForDuplicateEntries(PersistableBusinessObject businessObject, MaintainableCollectionDefinition maintainableCollectionDefinition) {
690         final String collectionName = maintainableCollectionDefinition.getName();
691 
692         if (maintainableCollectionDefinition.dissallowDuplicateKey()) {
693             final Class maintainableBusinessObjectClass = businessObject.getClass();
694             // validate that no duplicates based on keys exist
695             Collection<PersistableBusinessObject> collection = (Collection) ObjectUtils.getPropertyValue(businessObject, collectionName);
696             if (collection != null && !collection.isEmpty()) {
697                 final String propertyName = maintainableCollectionDefinition.getAttributeToHighlightOnDuplicateKey();
698                 // get collection label for dd
699                 final String label = dataDictionaryService.getCollectionLabel(maintainableBusinessObjectClass, collectionName);
700                 final String shortLabel = dataDictionaryService.getCollectionShortLabel(maintainableBusinessObjectClass, collectionName);
701                 int pos = 0;
702                 for (PersistableBusinessObject element : collection) {
703                     String pathToElement = collectionName + "[" + (pos++) + "]";
704                     if (ObjectUtils.countObjectsWithIdentitcalKey(collection, element) > 1) {
705                         GlobalVariables.getMessageMap().addToErrorPath(pathToElement);
706                         GlobalVariables.getMessageMap().putError(propertyName, RiceKeyConstants.ERROR_DUPLICATE_ELEMENT, new String[] { label, shortLabel });
707                         GlobalVariables.getMessageMap().removeFromErrorPath(pathToElement);
708                     }
709                 }
710 
711                 // recursivley check for duplicate entries on subcollections
712                 GlobalVariables.getMessageMap().addToErrorPath(collectionName);
713                 for (MaintainableCollectionDefinition nestedMaintainableCollectionDefinition : maintainableCollectionDefinition.getMaintainableCollections()) {
714                     for (PersistableBusinessObject element : collection) {
715                         validateMaintainableCollectionsForDuplicateEntries(element, nestedMaintainableCollectionDefinition);
716                     }
717                 }
718                 GlobalVariables.getMessageMap().removeFromErrorPath(collectionName);
719 
720             }
721         }
722     }
723        
724 	/**
725 	 * for issue KULRice 3072
726 	 * 
727 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getgetPreserveLockingKeysOnCopy(java.lang.Class)
728 	 */
729 	public boolean getPreserveLockingKeysOnCopy(Class businessObjectClass) {
730 
731 		boolean preserveLockingKeysOnCopy = false;
732 
733 		MaintenanceDocumentEntry docEntry = getMaintenanceDocumentEntry(businessObjectClass);
734 		
735 		if (docEntry != null) {
736 			preserveLockingKeysOnCopy = docEntry.getPreserveLockingKeysOnCopy();
737 		}
738 		
739 		return preserveLockingKeysOnCopy;
740 	}
741 
742 	/**
743 	 * for isue KULRice 3070
744 	 * 
745 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsRecordDeletion(java.lang.Class)
746 	 */
747 	public Boolean getAllowsRecordDeletion(Class businessObjectClass) {
748 		
749 		Boolean allowsRecordDeletion = Boolean.FALSE;
750 
751 		MaintenanceDocumentEntry docEntry = getMaintenanceDocumentEntry(businessObjectClass);
752 		
753 		if (docEntry != null) {
754 			allowsRecordDeletion = Boolean.valueOf(docEntry.getAllowsRecordDeletion());
755 		}
756 		
757 		return allowsRecordDeletion;
758 	}
759 
760 	/**
761 	 *  for issue KULRice3070, see if need delete button
762 	 * 
763 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getAllowsRecordDeletion(org.kuali.rice.krad.maintenance.MaintenanceDocument)
764 	 */
765 	public Boolean getAllowsRecordDeletion(MaintenanceDocument document) {
766         return document != null ? this.getAllowsRecordDeletion(document.getNewMaintainableObject().getBoClass()) : Boolean.FALSE;
767 	}
768 
769 	/**
770 	 * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#translateCodes(java.lang.Class)
771 	 */
772 	public Boolean translateCodes(Class businessObjectClass) {
773 		boolean translateCodes = false;
774 
775 		MaintenanceDocumentEntry docEntry = getMaintenanceDocumentEntry(businessObjectClass);
776 
777 		if (docEntry != null) {
778 			translateCodes = docEntry.isTranslateCodes();
779 		}
780 
781 		return translateCodes;
782 	}
783 
784 }