001/**
002 * Copyright 2005-2018 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.krad.maintenance;
017
018import java.io.Serializable;
019import java.security.GeneralSecurityException;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.commons.collections.CollectionUtils;
027import org.apache.commons.collections.MapUtils;
028import org.apache.commons.lang.StringUtils;
029import org.kuali.rice.core.api.CoreApiServiceLocator;
030import org.kuali.rice.core.api.encryption.EncryptionService;
031import org.kuali.rice.kim.api.identity.Person;
032import org.kuali.rice.krad.bo.AdHocRoutePerson;
033import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
034import org.kuali.rice.krad.bo.BusinessObject;
035import org.kuali.rice.krad.bo.DocumentHeader;
036import org.kuali.rice.krad.bo.Note;
037import org.kuali.rice.krad.data.CompoundKey;
038import org.kuali.rice.krad.data.DataObjectService;
039import org.kuali.rice.krad.data.DataObjectWrapper;
040import org.kuali.rice.krad.exception.PessimisticLockingException;
041import org.kuali.rice.krad.rules.rule.event.AddCollectionLineEvent;
042import org.kuali.rice.krad.service.DataObjectAuthorizationService;
043import org.kuali.rice.krad.service.DocumentDictionaryService;
044import org.kuali.rice.krad.service.KRADServiceLocator;
045import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
046import org.kuali.rice.krad.service.KualiRuleService;
047import org.kuali.rice.krad.service.LegacyDataAdapter;
048import org.kuali.rice.krad.service.MaintenanceDocumentService;
049import org.kuali.rice.krad.uif.UifConstants;
050import org.kuali.rice.krad.uif.UifPropertyPaths;
051import org.kuali.rice.krad.uif.component.BindingInfo;
052import org.kuali.rice.krad.uif.container.CollectionGroup;
053import org.kuali.rice.krad.uif.field.DataField;
054import org.kuali.rice.krad.uif.field.InputField;
055import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
056import org.kuali.rice.krad.uif.service.ViewHelperService;
057import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl;
058import org.kuali.rice.krad.uif.util.LifecycleElement;
059import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
060import org.kuali.rice.krad.uif.view.MaintenanceDocumentView;
061import org.kuali.rice.krad.uif.view.View;
062import org.kuali.rice.krad.uif.view.ViewModel;
063import org.kuali.rice.krad.util.KRADConstants;
064import org.kuali.rice.krad.util.KRADUtils;
065import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
066
067/**
068 * Default implementation of the <code>Maintainable</code> interface.
069 *
070 * @author Kuali Rice Team (rice.collab@kuali.org)
071 */
072public class MaintainableImpl extends ViewHelperServiceImpl implements Maintainable {
073    private static final long serialVersionUID = 9125271369161634992L;
074    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintainableImpl.class);
075
076    private String documentNumber;
077    private Object dataObject;
078    private Class<?> dataObjectClass;
079    private String maintenanceAction;
080
081    private transient LegacyDataAdapter legacyDataAdapter;
082    private transient DataObjectAuthorizationService dataObjectAuthorizationService;
083    private transient DocumentDictionaryService documentDictionaryService;
084    private transient EncryptionService encryptionService;
085    private transient DataObjectService dataObjectService;
086    private transient MaintenanceDocumentService maintenanceDocumentService;
087    private transient KualiRuleService kualiRuleService;
088
089    /**
090     * @see org.kuali.rice.krad.maintenance.Maintainable#retrieveObjectForEditOrCopy(MaintenanceDocument, java.util.Map)
091     */
092    @Override
093    public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
094        Object dataObject = null;
095        if ( getDataObjectService().supports(getDataObjectClass())) {
096            Map<String, Object> translatedValues = KRADUtils.coerceRequestParameterTypes(getDataObjectClass(), dataObjectKeys);
097            dataObject = getDataObjectService().find(getDataObjectClass(), new CompoundKey(translatedValues));
098        } else {
099            try {
100                dataObject = getLegacyDataAdapter().findObjectBySearch(getDataObjectClass(), dataObjectKeys);
101            } catch (Exception ex) {
102                if ( ex.getClass().equals( LegacyDataAdapter.CLASS_NOT_PERSISTABLE_OJB_EXCEPTION_CLASS )
103                        && !document.getOldMaintainableObject().isExternalBusinessObject()) {
104                    throw new RuntimeException("Data Object Class: "
105                            + getDataObjectClass()
106                            + " is not persistable and is not externalizable - configuration error");
107                }
108                // otherwise, let fall through
109            }
110        }
111
112        return dataObject;
113    }
114
115
116    /**
117     * @see org.kuali.rice.krad.maintenance.Maintainable#setDocumentNumber
118     */
119    @Override
120    public void setDocumentNumber(String documentNumber) {
121        this.documentNumber = documentNumber;
122    }
123
124    /**
125     * @see org.kuali.rice.krad.maintenance.Maintainable#getDocumentTitle
126     */
127    @Override
128    public String getDocumentTitle(MaintenanceDocument document) {
129        // default implementation is to allow MaintenanceDocumentBase to
130        // generate the doc title
131        return "";
132    }
133
134    /**
135     * @see org.kuali.rice.krad.maintenance.Maintainable#getDataObject
136     */
137    @Override
138    public Object getDataObject() {
139        return dataObject;
140    }
141
142    /**
143     * @see org.kuali.rice.krad.maintenance.Maintainable#setDataObject
144     */
145    @Override
146    public void setDataObject(Object object) {
147        this.dataObject = object;
148    }
149
150    /**
151     * @see org.kuali.rice.krad.maintenance.Maintainable#getDataObjectClass
152     */
153    @Override
154    public Class<?> getDataObjectClass() {
155        return dataObjectClass;
156    }
157
158    /**
159     * @see org.kuali.rice.krad.maintenance.Maintainable#setDataObjectClass
160     */
161    @Override
162    public void setDataObjectClass(Class<?> dataObjectClass) {
163        this.dataObjectClass = dataObjectClass;
164    }
165
166    /**
167     * Persistable business objects are lockable.
168     *
169     * @deprecated note used by Rice framework
170     */
171    @Override
172    @Deprecated
173    public boolean isLockable() {
174        return KRADServiceLocatorWeb.getLegacyDataAdapter().isLockable(getDataObject());
175    }
176
177//    /**
178//     * Returns the data object if its persistable, null otherwise.
179//     *
180//     * @deprecated this method has been left for compatibility reasons, use getDataObject instead.
181//     */
182//    @Override
183//    @Deprecated // Uses KNS Classes
184//    public PersistableBusinessObject getPersistableBusinessObject() {
185//        return KRADServiceLocatorWeb.getLegacyDataAdapter().toPersistableBusinessObject(getDataObject());
186//    }
187
188    /**
189     * @see org.kuali.rice.krad.maintenance.Maintainable#getMaintenanceAction
190     */
191    @Override
192    public String getMaintenanceAction() {
193        return maintenanceAction;
194    }
195
196    /**
197     * @see org.kuali.rice.krad.maintenance.Maintainable#setMaintenanceAction
198     */
199    @Override
200    public void setMaintenanceAction(String maintenanceAction) {
201        this.maintenanceAction = maintenanceAction;
202    }
203
204    /**
205     * Note: as currently implemented, every key field for a given
206     * data object class must have a visible getter.
207     *
208     * @see org.kuali.rice.krad.maintenance.Maintainable#generateMaintenanceLocks
209     */
210    @Override
211    public List<MaintenanceLock> generateMaintenanceLocks() {
212        return generateMaintenanceLocks(getDocumentNumber(), getDocumentTypeName(), getDataObjectClass(), getDataObject());
213    }
214
215    /**
216     * Allows locking of maintenance objects other than the one of the current maintenance object.
217     *
218     * @param documentNumber of the locking maintenance document
219     * @param documentTypeName of the maintenance document to be locked
220     * @param dataObjectClass of the maintenance document to be locked
221     * @param dataObject of the maintenance document to be locked
222     * @return
223     */
224    protected List<MaintenanceLock> generateMaintenanceLocks(String documentNumber, String documentTypeName, Class<?> dataObjectClass, Object dataObject) {
225        List<MaintenanceLock> maintenanceLocks = new ArrayList<MaintenanceLock>();
226        StringBuffer lockRepresentation = new StringBuffer(dataObjectClass.getName());
227        lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_CLASS_DELIM);
228
229        DataObjectWrapper<Object> wrapper = getDataObjectService().wrap(dataObject);
230
231        List<String> keyFieldNames = getDocumentDictionaryService().getLockingKeys(documentTypeName);
232
233        for (Iterator<?> i = keyFieldNames.iterator(); i.hasNext(); ) {
234            String fieldName = (String) i.next();
235
236            Object fieldValue = wrapper.getPropertyValueNullSafe(fieldName);
237            if (fieldValue == null) {
238                fieldValue = "";
239            }
240
241            // check if field is a secure
242            if (getDataObjectAuthorizationService()
243                    .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObjectClass, fieldName)) {
244                try {
245                    if(CoreApiServiceLocator.getEncryptionService().isEnabled()) {
246                        fieldValue = getEncryptionService().encrypt(fieldValue);
247                    }
248                } catch (GeneralSecurityException e) {
249                    LOG.error("Unable to encrypt secure field for locking representation " + e.getMessage());
250                    throw new RuntimeException(
251                            "Unable to encrypt secure field for locking representation " + e.getMessage());
252                }
253            }
254
255            lockRepresentation.append(fieldName);
256            lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_FIELDNAME_DELIM);
257            lockRepresentation.append(String.valueOf(fieldValue));
258            if (i.hasNext()) {
259                lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_VALUE_DELIM);
260            }
261        }
262
263        MaintenanceLock maintenanceLock = new MaintenanceLock();
264        maintenanceLock.setDocumentNumber(documentNumber);
265        maintenanceLock.setLockingRepresentation(lockRepresentation.toString());
266        maintenanceLocks.add(maintenanceLock);
267
268        return maintenanceLocks;
269    }
270
271    /**
272     * Retrieves the document type name from the data dictionary based on
273     * business object class
274     */
275    protected String getDocumentTypeName() {
276        return getDocumentDictionaryService().getMaintenanceDocumentTypeName(dataObjectClass);
277    }
278
279    /**
280     * @see org.kuali.rice.krad.maintenance.Maintainable#saveDataObject
281     */
282    @Override
283    public void saveDataObject() {
284        if ( dataObject == null ) {
285            LOG.warn( "dataObject in maintainable was null - this should not be the case.  Skipping saveDataObject()");
286            return;
287        }
288        dataObject = getLegacyDataAdapter().linkAndSave((Serializable)dataObject);
289    }
290
291    /**
292     * @see org.kuali.rice.krad.maintenance.Maintainable#deleteDataObject
293     */
294    @Override
295    public void deleteDataObject() {
296        if (dataObject == null) {
297            return;
298        }
299        getLegacyDataAdapter().delete(dataObject);
300    }
301
302    /**
303     * @see org.kuali.rice.krad.maintenance.Maintainable#doRouteStatusChange
304     */
305    @Override
306    public void doRouteStatusChange(DocumentHeader documentHeader) {
307        // no default implementation
308    }
309
310    /**
311     * @see org.kuali.rice.krad.maintenance.Maintainable#getLockingDocumentId
312     */
313    @Override
314    public String getLockingDocumentId() {
315        return getMaintenanceDocumentService().getLockingDocumentId(this, documentNumber);
316    }
317
318    /**
319     * @see org.kuali.rice.krad.maintenance.Maintainable#getWorkflowEngineDocumentIdsToLock
320     */
321    @Override
322    public List<String> getWorkflowEngineDocumentIdsToLock() {
323        return null;
324    }
325
326    /**
327     * Default implementation simply returns false to indicate that custom
328     * lock descriptors are not supported by MaintainableImpl. If custom
329     * lock descriptors are needed, the appropriate subclasses should override
330     * this method
331     *
332     * @see org.kuali.rice.krad.maintenance.Maintainable#useCustomLockDescriptors
333     */
334    @Override
335    public boolean useCustomLockDescriptors() {
336        return false;
337    }
338
339    /**
340     * Default implementation just throws a PessimisticLockingException.
341     * Subclasses of MaintainableImpl that need support for custom lock
342     * descriptors should override this method
343     *
344     * @see org.kuali.rice.krad.maintenance.Maintainable#getCustomLockDescriptor
345     */
346    @Override
347    public String getCustomLockDescriptor(Person user) {
348        throw new PessimisticLockingException("The Maintainable for document " + documentNumber +
349                " is using pessimistic locking with custom lock descriptors, but the Maintainable has not overridden the getCustomLockDescriptor method");
350    }
351
352    /**
353     * @see org.kuali.rice.krad.maintenance.Maintainable#isNotesEnabled
354     */
355    @Override
356    public boolean isNotesEnabled() {
357        return getLegacyDataAdapter().areNotesSupported(dataObjectClass);
358    }
359
360    /**
361     * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isExternalBusinessObject
362     */
363    @Override
364    public boolean isExternalBusinessObject() {
365        return false;
366    }
367
368    /**
369     * @see org.kuali.rice.krad.maintenance.MaintainableImpl#prepareExternalBusinessObject
370     */
371    @Override
372    @Deprecated
373    public void prepareExternalBusinessObject(BusinessObject businessObject) {
374        // by default do nothing
375    }
376
377    /**
378     * Checks whether the data object is not null and has its primary key values populated.
379     *
380     * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isOldDataObjectInDocument
381     */
382    @Override
383    public boolean isOldDataObjectInDocument() {
384        boolean isOldDataObjectInExistence = true;
385
386        if (getDataObject() == null) {
387            isOldDataObjectInExistence = false;
388        } else {
389            Map<String, ?> keyFieldValues = getLegacyDataAdapter().getPrimaryKeyFieldValuesDOMDS(getDataObject());
390            for (Object keyValue : keyFieldValues.values()) {
391                if (keyValue == null) {
392                    isOldDataObjectInExistence = false;
393                } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) {
394                    isOldDataObjectInExistence = false;
395                }
396
397                if (!isOldDataObjectInExistence) {
398                    break;
399                }
400            }
401        }
402
403        return isOldDataObjectInExistence;
404    }
405
406    /**
407     * @see org.kuali.rice.krad.maintenance.Maintainable#prepareForSave
408     */
409    @Override
410    public void prepareForSave() {
411        // by default do nothing
412    }
413
414    /**
415     * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterRetrieve
416     */
417    @Override
418    public void processAfterRetrieve() {
419        // by default do nothing
420    }
421
422    /**
423     * @see org.kuali.rice.krad.maintenance.MaintainableImpl#setupNewFromExisting
424     */
425    @Override
426    public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) {
427        // by default do nothing
428    }
429
430    /**
431     * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterCopy
432     */
433    @Override
434    public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> requestParameters) {
435        // by default do nothing
436    }
437
438    /**
439     * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterEdit
440     */
441    @Override
442    public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
443        // by default do nothing
444    }
445
446    /**
447     * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterNew
448     */
449    @Override
450    public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
451        // by default do nothing
452    }
453
454    /**
455     * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterPost
456     */
457    @Override
458    public void processAfterPost(MaintenanceDocument document, Map<String, String[]> requestParameters) {
459        // by default do nothing
460    }
461
462    /**
463     * In the case of edit maintenance adds a new blank line to the old side.
464     *
465     * TODO: should this write some sort of missing message on the old side
466     * instead?
467     *
468     */
469    @Override
470    public void processAfterAddLine(ViewModel viewModel, Object addLine, String collectionId, String collectionPath,
471                boolean isValidLine) {
472        super.processAfterAddLine(viewModel, addLine, collectionId, collectionPath, isValidLine);
473
474        // Check for maintenance documents in edit but exclude notes and ad hoc recipients
475        if (viewModel instanceof MaintenanceDocumentForm
476                && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(
477                ((MaintenanceDocumentForm) viewModel).getMaintenanceAction())
478                && !(addLine instanceof Note)
479                && !(addLine instanceof AdHocRoutePerson)
480                && !(addLine instanceof AdHocRouteWorkgroup)) {
481            MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) viewModel;
482            MaintenanceDocument document = maintenanceForm.getDocument();
483
484            BindingInfo bindingInfo = (BindingInfo) viewModel.getViewPostMetadata().getComponentPostData(collectionId,
485                    UifConstants.PostMetadata.BINDING_INFO);
486
487            // get the old object's collection
488            //KULRICE-7970 support multiple level objects
489            String bindingPrefix = bindingInfo.getBindByNamePrefix();
490            String propertyPath = bindingInfo.getBindingName();
491            if (bindingPrefix != "" && bindingPrefix != null) {
492                propertyPath = bindingPrefix + "." + propertyPath;
493            }
494
495            Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(
496                    document.getOldMaintainableObject().getDataObject(), propertyPath);
497
498            Class<?> collectionObjectClass = (Class<?>) viewModel.getViewPostMetadata().getComponentPostData(collectionId,
499                    UifConstants.PostMetadata.COLL_OBJECT_CLASS);
500
501            try {
502                Object blankLine = collectionObjectClass.newInstance();
503                //Add a blank line to the top of the collection
504                if (oldCollection instanceof List) {
505                    ((List<Object>) oldCollection).add(0, blankLine);
506                } else {
507                    oldCollection.add(blankLine);
508                }
509            } catch (Exception e) {
510                throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
511            }
512        }
513    }
514
515    /**
516     * In the case of edit maintenance deleted the item on the old side.
517     *
518     * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterDeleteLine(org.kuali.rice.krad.uif.view.ViewModel, String, String, int)
519     */
520    @Override
521    public void processAfterDeleteLine(ViewModel model, String collectionId, String collectionPath, int lineIndex) {
522        super.processAfterDeleteLine(model, collectionId, collectionPath, lineIndex);
523
524        Class<?> collectionObjectClass = (Class<?>) model.getViewPostMetadata().getComponentPostData(collectionId,
525                UifConstants.PostMetadata.COLL_OBJECT_CLASS);
526
527        // Check for maintenance documents in edit but exclude notes and ad hoc recipients
528        if (model instanceof MaintenanceDocumentForm
529                && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm)model).getMaintenanceAction())
530                && !collectionObjectClass.getName().equals(Note.class.getName())
531                && !collectionObjectClass.getName().equals(AdHocRoutePerson.class.getName())
532                && !collectionObjectClass.getName().equals(AdHocRouteWorkgroup.class.getName())) {
533            MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
534            MaintenanceDocument document = maintenanceForm.getDocument();
535
536            BindingInfo bindingInfo = (BindingInfo) model.getViewPostMetadata().getComponentPostData(collectionId,
537                    UifConstants.PostMetadata.BINDING_INFO);
538
539            // get the old object's collection
540            //KULRICE-7970 support multiple level objects
541            String bindingPrefix = bindingInfo.getBindByNamePrefix();
542            String propertyPath = bindingInfo.getBindingName();
543            if (bindingPrefix != "" && bindingPrefix != null) {
544                propertyPath = bindingPrefix + "." + propertyPath;
545            }
546
547            Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(
548                                document.getOldMaintainableObject().getDataObject(), propertyPath);
549
550            try {
551                // Remove the object at lineIndex from the collection
552                oldCollection.remove(oldCollection.toArray()[lineIndex]);
553            } catch (Exception e) {
554                throw new RuntimeException("Unable to delete line instance for old maintenance object", e);
555            }
556        }
557    }
558
559    @Override
560    protected boolean performAddLineValidation(ViewModel viewModel, Object newLine, String collectionId,
561                String collectionPath) {
562        boolean isValidLine = super.performAddLineValidation(viewModel, newLine, collectionId, collectionPath);
563
564        BindingInfo bindingInfo = (BindingInfo) viewModel.getViewPostMetadata().getComponentPostData(collectionId,
565                            UifConstants.PostMetadata.BINDING_INFO);
566
567        if (viewModel instanceof MaintenanceDocumentForm) {
568            MaintenanceDocumentForm form = ((MaintenanceDocumentForm) viewModel);
569            isValidLine &= getKualiRuleService()
570                    .applyRules(new AddCollectionLineEvent(form.getDocument(), bindingInfo.getBindingName(), newLine));
571        }
572
573        return isValidLine;
574    }
575
576    /**
577     * Retrieves the document number configured on this maintainable.
578     *
579     * @return String document number
580     */
581    protected String getDocumentNumber() {
582        return this.documentNumber;
583    }
584
585
586
587    /**
588     * Hook for service overrides to perform custom apply model logic on the component.
589     *
590     * @param element element instance to apply model to
591     * @param model Top level object containing the data (could be the model or a top level business
592     *        object, dto)
593     */
594    @Override
595    public void performCustomApplyModel(LifecycleElement element, Object model) {
596
597        MaintenanceDocumentForm form = (MaintenanceDocumentForm) model;
598
599        /**
600         *  Primary keys should not be editable on maintenance edit action.
601         *
602         *  Determines if the maintenance action matches MAINTENANCE_EDIT_ACTION, that the element is of type InputField and
603         *  if the bindingPath includes a new maintainable path
604         */
605        if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(form.getMaintenanceAction()) && element instanceof InputField
606                && StringUtils.contains(((InputField) element).getName(), KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)
607                && !(StringUtils.contains(((InputField) element).getName(), UifPropertyPaths.NEW_COLLECTION_LINES))) {
608            setPrimaryKeyReadOnly(element);
609
610        }
611    }
612
613    /**
614     * sets primary keys to read-only
615     */
616     private void setPrimaryKeyReadOnly(LifecycleElement element){
617
618         String propertyName =  ((InputField) element).getPropertyName();
619         MaintenanceDocumentView maintenanceView = (MaintenanceDocumentView) ViewLifecycle.getView();
620
621         /**
622          *   get a list of primary keys from the maintenance view dataObject
623          */
624         List<String> primaryKeys = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(maintenanceView.getDataObjectClassName());
625
626         /**
627          *  loop thru primary keys, match to our component field name and set it to read-only
628          */
629         for (String field : primaryKeys) {
630             if(propertyName.equals(field)){
631                 ((InputField) element).setReadOnly(true);
632
633             }
634         }
635     }
636
637    /**
638     * For the copy action, clears out primary key values, applies defaults to previously cleared fields,
639     * and replaces any new fields that the current user is unauthorized for with default values in the old record.
640     *
641     * {@inheritDoc}
642     */
643    @Override
644    public void performCustomFinalize(LifecycleElement element, Object model, LifecycleElement parent) {
645        if (!(model instanceof MaintenanceDocumentForm)) {
646            return;
647        }
648
649        MaintenanceDocumentForm form = (MaintenanceDocumentForm) model;
650
651        if (form.getDocument().isFieldsClearedOnCopy()) {
652            return;
653        }
654
655        if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(form.getMaintenanceAction())) {
656            View view = ViewLifecycle.getView();
657
658            if (element instanceof DataField) {
659                DataField field = (DataField) element;
660
661                applyDefaultValuesForPreviouslyClearedFields(view, form, field);
662
663                clearUnauthorizedField(view, form, field);
664            } else if (element instanceof CollectionGroup) {
665                CollectionGroup group = (CollectionGroup) element;
666
667                clearUnauthorizedLine(view, form, group);
668            }
669        }
670    }
671
672    /**
673     * For the copy action, runs the custom processing after the copy and sets the indicator that fields have been
674     * copied as true.
675     *
676     * {@inheritDoc}
677     */
678    @Override
679    public void performCustomViewFinalize(Object model) {
680        if (!(model instanceof MaintenanceDocumentForm)) {
681            return;
682        }
683
684        MaintenanceDocumentForm form = (MaintenanceDocumentForm) model;
685
686        if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(form.getMaintenanceAction())) {
687            processAfterCopy(form.getDocument(), form.getInitialRequestParameters());
688
689            form.getDocument().setFieldsClearedOnCopy(true);
690        }
691    }
692
693    /**
694     * Applies the default value of a field if it was a field that was previously cleared.
695     *
696     * @param view view instance that contains the fields being checked
697     * @param model model instance that contains the fields being checked
698     * @param field field being checked to see if it has been cleared
699     */
700    private void applyDefaultValuesForPreviouslyClearedFields(View view, ViewModel model, DataField field) {
701        List<String> clearValueOnCopyPropertyNames =
702                KRADServiceLocatorWeb.getDocumentDictionaryService().getClearValueOnCopyPropertyNames(
703                        ((MaintenanceDocumentView) view).getDataObjectClassName());
704
705        for (String clearValueOnCopyPropertyName : clearValueOnCopyPropertyNames) {
706            if (field.getPropertyName().equalsIgnoreCase(clearValueOnCopyPropertyName)) {
707                String bindingPath = field.getBindingInfo().getBindingPath();
708
709                view.getViewHelperService().populateDefaultValueForField(model, field, bindingPath);
710            }
711        }
712    }
713
714    /**
715     * Determines if the current field is restricted and replaces its value with a default value if so.
716     * This method should only be called on a copy operation as it checks for canCopyOnReadOnly
717     *
718     * @param view view instance that contains the fields being checked
719     * @param model model instance that contains the fields being checked
720     * @param field field being checked for restrictions
721     */
722    private void clearUnauthorizedField(View view, ViewModel model, DataField field) {
723        ViewHelperService helper = ViewLifecycle.getHelper();
724        String bindingPath = field.getBindingInfo().getBindingPath();
725
726        if (StringUtils.contains(bindingPath, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)) {
727            // The field is restricted if it is hidden or read only or masked
728            boolean isRestricted = field.isHidden() || (Boolean.TRUE.equals(field.getReadOnly()) && !Boolean.TRUE.equals(field.getCanCopyOnReadOnly())) || field.isApplyMask();
729
730            // If the default value is a sequence number set isRestricted to false since the new sequence number has
731            // already been retrieved.  We don't want to set it to null and fetch it again.
732            Map<String, String> propertyExpressions = field.getPropertyExpressions();
733            if (isRestricted && propertyExpressions.containsKey(UifConstants.ComponentProperties.DEFAULT_VALUE)) {
734                String propertyExpression = propertyExpressions.get(UifConstants.ComponentProperties.DEFAULT_VALUE);
735                if (StringUtils.contains(propertyExpression, UifConstants.SEQUENCE_PREFIX)) {
736                    isRestricted = false;
737                }
738            }
739
740            // If just the field (not its containing line) is restricted, clear it out and apply default values
741            if (isRestricted && !isLineRestricted(field)) {
742                if (ObjectPropertyUtils.isWritableProperty(model, bindingPath)) {
743                    ObjectPropertyUtils.setPropertyValue(model, bindingPath, null);
744                }
745
746                field.setReadOnlyDisplaySuffixPropertyName(null);
747                field.setReadOnlyDisplaySuffix(null);
748
749                helper.populateDefaultValueForField(model, field, bindingPath);
750            }
751        }
752    }
753
754    /**
755     * Returns whether a line that contains a field is restricted; that is, if the field is part of a group and that
756     * group has some unauthorized binding information.
757     *
758     * @param field field being checked for restrictions
759     *
760     * @return true if the field is in a line with restrictions, false otherwise
761     */
762    private boolean isLineRestricted(DataField field) {
763        CollectionGroup group = (CollectionGroup) MapUtils.getObject(field.getContext(),
764                UifConstants.ContextVariableNames.COLLECTION_GROUP);
765
766        return group != null && CollectionUtils.isNotEmpty(group.getUnauthorizedLineBindingInfos());
767    }
768
769    /**
770     * Determines if the current group contains restricted lines and clears them if so.
771     * This method should only be called on a copy operation as it checks for canCopyOnReadOnly
772     *
773     * @param view view instance that contains the group being checked
774     * @param model model instance that contains the group being checked
775     * @param group group being checked for restrictions
776     */
777    private void clearUnauthorizedLine(View view, ViewModel model, CollectionGroup group) {
778        String bindingPath = group.getBindingInfo().getBindingPath();
779
780        if (StringUtils.contains(bindingPath, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)) {
781            // A line is restricted if it is hidden or read only
782            if (group.getUnauthorizedLineBindingInfos() != null && !group.getCanCopyOnReadOnly()) {
783                Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, bindingPath);
784
785                // If any lines are restricted, clear them out
786                for (BindingInfo bindingInfo : group.getUnauthorizedLineBindingInfos()) {
787                    String lineBindingPath = bindingInfo.getBindingPath();
788                    Object line = ObjectPropertyUtils.getPropertyValue(model, lineBindingPath);
789
790                    collection.remove(line);
791                }
792            }
793        }
794    }
795
796    @Override
797    @Deprecated // KNS Service
798    protected LegacyDataAdapter getLegacyDataAdapter() {
799        if (legacyDataAdapter == null) {
800            legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter();
801        }
802        return this.legacyDataAdapter;
803    }
804
805    @Override
806    @Deprecated // KNS Service
807    public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) {
808        this.legacyDataAdapter = legacyDataAdapter;
809    }
810
811    protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
812        if (dataObjectAuthorizationService == null) {
813            this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService();
814        }
815        return dataObjectAuthorizationService;
816    }
817
818    public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) {
819        this.dataObjectAuthorizationService = dataObjectAuthorizationService;
820    }
821
822    public DocumentDictionaryService getDocumentDictionaryService() {
823        if (documentDictionaryService == null) {
824            this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
825        }
826        return documentDictionaryService;
827    }
828
829    public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
830        this.documentDictionaryService = documentDictionaryService;
831    }
832
833    protected EncryptionService getEncryptionService() {
834        if (encryptionService == null) {
835            encryptionService = CoreApiServiceLocator.getEncryptionService();
836        }
837        return encryptionService;
838    }
839
840    public void setEncryptionService(EncryptionService encryptionService) {
841        this.encryptionService = encryptionService;
842    }
843
844    @Override
845    protected DataObjectService getDataObjectService() {
846        if (dataObjectService == null) {
847            dataObjectService = KRADServiceLocator.getDataObjectService();
848        }
849        return dataObjectService;
850    }
851
852    protected MaintenanceDocumentService getMaintenanceDocumentService() {
853        if (maintenanceDocumentService == null) {
854            maintenanceDocumentService = KRADServiceLocatorWeb.getMaintenanceDocumentService();
855        }
856        return maintenanceDocumentService;
857    }
858
859    public void setMaintenanceDocumentService(MaintenanceDocumentService maintenanceDocumentService) {
860        this.maintenanceDocumentService = maintenanceDocumentService;
861    }
862
863    public KualiRuleService getKualiRuleService() {
864        if (kualiRuleService == null) {
865            kualiRuleService = KRADServiceLocatorWeb.getKualiRuleService();
866        }
867        return kualiRuleService;
868    }
869
870    public void setKualiRuleService(KualiRuleService kualiRuleService) {
871        this.kualiRuleService = kualiRuleService;
872    }
873
874    @Override
875    public Object getPersistableBusinessObject() {
876        return getDataObject();
877    }
878}