001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kns.service.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.util.RiceKeyConstants;
020import org.kuali.rice.kew.api.KewApiServiceLocator;
021import org.kuali.rice.kew.api.doctype.DocumentType;
022import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition;
023import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
024import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition;
025import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition;
026import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
027import org.kuali.rice.kns.document.MaintenanceDocument;
028import org.kuali.rice.kns.maintenance.Maintainable;
029import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
030import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
031import org.kuali.rice.krad.bo.PersistableBusinessObject;
032import org.kuali.rice.krad.datadictionary.DataDictionary;
033import org.kuali.rice.krad.rules.rule.BusinessRule;
034import org.kuali.rice.krad.service.DataDictionaryService;
035import org.kuali.rice.krad.util.GlobalVariables;
036import org.kuali.rice.krad.util.ObjectUtils;
037import org.kuali.rice.krad.valuefinder.ValueFinder;
038
039import java.util.ArrayList;
040import java.util.Collection;
041import java.util.Iterator;
042import java.util.List;
043
044/**
045 * This class is the service implementation for the MaintenanceDocumentDictionary structure. Defines the API for the interacting
046 * with Document-related entries in the data dictionary. This is the default implementation, that is delivered with Kuali.
047 */
048@Deprecated
049public class MaintenanceDocumentDictionaryServiceImpl implements MaintenanceDocumentDictionaryService {
050    protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintenanceDocumentDictionaryServiceImpl.class);
051
052    private DataDictionaryService dataDictionaryService;
053
054    /**
055     * Gets the workflow document type for the given documentTypeName
056     * 
057     * @param documentTypeName
058     * @return
059     */
060    protected DocumentType getDocumentType(String documentTypeName) {
061        return KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(documentTypeName);
062    }
063
064    /**
065     * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintenanceLabel(java.lang.String)
066     */
067    public String getMaintenanceLabel(String docTypeName) {
068        String label = null;
069
070        DocumentType docType = getDocumentType(docTypeName);
071        if (docType != null) {
072            label = docType.getLabel();
073        }
074
075        return label;
076    }
077
078    /**
079     * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintenanceDescription(java.lang.String)
080     */
081    public String getMaintenanceDescription(String docTypeName) {
082        String description = null;
083
084        DocumentType docType = getDocumentType(docTypeName);
085        if (docType != null) {
086            description = docType.getDescription();
087        }
088
089        return description;
090    }
091
092    /**
093     * @see org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService#getMaintainableClass(java.lang.String)
094     */
095    @Deprecated
096    public Class getMaintainableClass(String docTypeName) {
097        Class maintainableClass = null;
098
099        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}