View Javadoc
1   /**
2    * Copyright 2005-2016 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.krad.maintenance;
17  
18  import java.io.Serializable;
19  import java.security.GeneralSecurityException;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.collections.CollectionUtils;
27  import org.apache.commons.collections.MapUtils;
28  import org.apache.commons.lang.StringUtils;
29  import org.kuali.rice.core.api.CoreApiServiceLocator;
30  import org.kuali.rice.core.api.encryption.EncryptionService;
31  import org.kuali.rice.kim.api.identity.Person;
32  import org.kuali.rice.krad.bo.AdHocRoutePerson;
33  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
34  import org.kuali.rice.krad.bo.BusinessObject;
35  import org.kuali.rice.krad.bo.DocumentHeader;
36  import org.kuali.rice.krad.bo.Note;
37  import org.kuali.rice.krad.data.CompoundKey;
38  import org.kuali.rice.krad.data.DataObjectService;
39  import org.kuali.rice.krad.data.DataObjectWrapper;
40  import org.kuali.rice.krad.exception.PessimisticLockingException;
41  import org.kuali.rice.krad.rules.rule.event.AddCollectionLineEvent;
42  import org.kuali.rice.krad.service.DataObjectAuthorizationService;
43  import org.kuali.rice.krad.service.DocumentDictionaryService;
44  import org.kuali.rice.krad.service.KRADServiceLocator;
45  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
46  import org.kuali.rice.krad.service.KualiRuleService;
47  import org.kuali.rice.krad.service.LegacyDataAdapter;
48  import org.kuali.rice.krad.service.MaintenanceDocumentService;
49  import org.kuali.rice.krad.uif.UifConstants;
50  import org.kuali.rice.krad.uif.UifPropertyPaths;
51  import org.kuali.rice.krad.uif.component.BindingInfo;
52  import org.kuali.rice.krad.uif.container.CollectionGroup;
53  import org.kuali.rice.krad.uif.field.DataField;
54  import org.kuali.rice.krad.uif.field.InputField;
55  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
56  import org.kuali.rice.krad.uif.service.ViewHelperService;
57  import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl;
58  import org.kuali.rice.krad.uif.util.LifecycleElement;
59  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
60  import org.kuali.rice.krad.uif.view.MaintenanceDocumentView;
61  import org.kuali.rice.krad.uif.view.View;
62  import org.kuali.rice.krad.uif.view.ViewModel;
63  import org.kuali.rice.krad.util.KRADConstants;
64  import org.kuali.rice.krad.util.KRADUtils;
65  import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
66  
67  /**
68   * Default implementation of the <code>Maintainable</code> interface.
69   *
70   * @author Kuali Rice Team (rice.collab@kuali.org)
71   */
72  public class MaintainableImpl extends ViewHelperServiceImpl implements Maintainable {
73      private static final long serialVersionUID = 9125271369161634992L;
74      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintainableImpl.class);
75  
76      private String documentNumber;
77      private Object dataObject;
78      private Class<?> dataObjectClass;
79      private String maintenanceAction;
80  
81      private transient LegacyDataAdapter legacyDataAdapter;
82      private transient DataObjectAuthorizationService dataObjectAuthorizationService;
83      private transient DocumentDictionaryService documentDictionaryService;
84      private transient EncryptionService encryptionService;
85      private transient DataObjectService dataObjectService;
86      private transient MaintenanceDocumentService maintenanceDocumentService;
87      private transient KualiRuleService kualiRuleService;
88  
89      /**
90       * @see org.kuali.rice.krad.maintenance.Maintainable#retrieveObjectForEditOrCopy(MaintenanceDocument, java.util.Map)
91       */
92      @Override
93      public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
94          Object dataObject = null;
95          if ( getDataObjectService().supports(getDataObjectClass())) {
96              Map<String, Object> translatedValues = KRADUtils.coerceRequestParameterTypes(getDataObjectClass(), dataObjectKeys);
97              dataObject = getDataObjectService().find(getDataObjectClass(), new CompoundKey(translatedValues));
98          } else {
99              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 }