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