/**
 * Copyright 2005-2018 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.krad.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.criteria.QueryByCriteria;
import org.kuali.rice.core.api.util.RiceConstants;
import org.kuali.rice.kim.api.KimConstants.PermissionNames;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kim.api.permission.PermissionService;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.kns.authorization.AuthorizationConstants;
import org.kuali.rice.krad.data.DataObjectService;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.document.authorization.PessimisticLock;
import org.kuali.rice.krad.exception.AuthorizationException;
import org.kuali.rice.krad.exception.PessimisticLockingException;
import org.kuali.rice.krad.service.DataDictionaryService;
import org.kuali.rice.krad.service.LegacyDataAdapter;
import org.kuali.rice.krad.service.PessimisticLockService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.KRADPropertyConstants;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service implementation for pessimistic locking
 *
 * @author Kuali Rice Team (rice.collab@kuali.org)
 */
@Transactional
public class PessimisticLockServiceImpl implements PessimisticLockService {
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PessimisticLockServiceImpl.class);

    protected DataObjectService dataObjectService;
    protected DataDictionaryService dataDictionaryService;
    protected PermissionService permissionService;
    protected PersonService personService;

	/**
     * @see org.kuali.rice.krad.service.PessimisticLockService#delete(java.lang.String)
     */
    @Override
	public void delete(String id) {
        if (StringUtils.isBlank(id)) {
            throw new IllegalArgumentException("An invalid blank id was passed to delete a Pessimistic Lock.");
        }
        PessimisticLock lock = dataObjectService.find(PessimisticLock.class, Long.valueOf(id));
        if ( lock == null ) {
            throw new IllegalArgumentException("Pessimistic Lock with id " + id + " cannot be found in the database.");
        }
        Person user = GlobalVariables.getUserSession().getPerson();
        if ( (!lock.isOwnedByUser(user)) && (!isPessimisticLockAdminUser(user)) ) {
            throw new AuthorizationException(user.getName(),"delete", "Pessimistick Lock (id " + id + ")");
        }
        delete(lock);
    }

    private void delete(PessimisticLock lock) {
    	if ( LOG.isDebugEnabled() ) {
    		LOG.debug("Deleting lock: " + lock);
    	}
        dataObjectService.delete(lock);
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(String)
     */
    @Override
	public PessimisticLock generateNewLock(String documentNumber) {
        return generateNewLock(documentNumber, GlobalVariables.getUserSession().getPerson());
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(java.lang.String)
     */
    @Override
	public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor) {
        return generateNewLock(documentNumber, lockDescriptor, GlobalVariables.getUserSession().getPerson());
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(java.lang.String, org.kuali.rice.kim.api.identity.Person)
     */
    @Override
	public PessimisticLock generateNewLock(String documentNumber, Person user) {
        return generateNewLock(documentNumber, PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, user);
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(java.lang.String, java.lang.String, org.kuali.rice.kim.api.identity.Person)
     */
    @Override
	public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor, Person user) {
        PessimisticLock lock = new PessimisticLock(documentNumber, lockDescriptor, user, GlobalVariables.getUserSession());
        lock = save(lock);
        if ( LOG.isDebugEnabled() ) {
        	LOG.debug("Generated new lock: " + lock);
        }
        return lock;
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#getPessimisticLocksForDocument(java.lang.String)
     */
    @Override
	public List<PessimisticLock> getPessimisticLocksForDocument(String documentNumber) {
        return new ArrayList<PessimisticLock>( dataObjectService.findMatching(PessimisticLock.class,
        		QueryByCriteria.Builder.forAttribute(KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber)
        		.build()).getResults() );
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#getPessimisticLocksForSession(java.lang.String)
     */
    @Override
	public List<PessimisticLock> getPessimisticLocksForSession(String sessionId) {
        return new ArrayList<PessimisticLock>( dataObjectService.findMatching(PessimisticLock.class,
        		QueryByCriteria.Builder.forAttribute(KRADPropertyConstants.SESSION_ID, sessionId)
        		.build()).getResults() );
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#isPessimisticLockAdminUser(org.kuali.rice.kim.api.identity.Person)
     */
    @Override
	public boolean isPessimisticLockAdminUser(Person user) {
    	return getPermissionService().isAuthorized( user.getPrincipalId(), KRADConstants.KNS_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING,
                Collections.<String, String>emptyMap() );
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#releaseAllLocksForUser(java.util.List, org.kuali.rice.kim.api.identity.Person)
     */
    @Override
	public void releaseAllLocksForUser(List<PessimisticLock> locks, Person user) {
        for (Iterator<PessimisticLock> iterator = locks.iterator(); iterator.hasNext();) {
            PessimisticLock lock = iterator.next();
            if (lock.isOwnedByUser(user)) {
                try {
                    delete(lock);
                } catch ( RuntimeException ex ) {
                	if ( ex.getCause() != null && ex.getCause().getClass().equals( LegacyDataAdapter.OPTIMISTIC_LOCK_OJB_EXCEPTION_CLASS ) ) {
                        LOG.warn( "Suppressing Optimistic Lock Exception. Document Num: " +  lock.getDocumentNumber());
                    } else {
                        throw ex;
                    }
                }
            }
        }
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#releaseAllLocksForUser(java.util.List, org.kuali.rice.kim.api.identity.Person, java.lang.String)
     */
    @Override
	public void releaseAllLocksForUser(List<PessimisticLock> locks, Person user, String lockDescriptor) {
        for (Iterator<PessimisticLock> iterator = locks.iterator(); iterator.hasNext();) {
            PessimisticLock lock = iterator.next();
            if ( (lock.isOwnedByUser(user)) && (lockDescriptor.equals(lock.getLockDescriptor())) ) {
                try {
                    delete(lock);
                } catch ( RuntimeException ex ) {
                	if ( ex.getCause() != null && ex.getCause().getClass().equals( LegacyDataAdapter.OPTIMISTIC_LOCK_OJB_EXCEPTION_CLASS ) ) {
                        LOG.warn( "Suppressing Optimistic Lock Exception. Document Num: " +  lock.getDocumentNumber());
                    } else {
                        throw ex;
                    }
                }
            }
        }
    }

    /**
     * @see org.kuali.rice.krad.service.PessimisticLockService#save(org.kuali.rice.krad.document.authorization.PessimisticLock)
     */
    @Override
	public PessimisticLock save(PessimisticLock lock) {
    	if ( LOG.isDebugEnabled() ) {
    		LOG.debug("Saving lock: " + lock);
    	}
        return dataObjectService.save(lock);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean establishPessimisticLocks(Document document, Person user, boolean canEdit) {
        // establish pessimistic locks if needed
        if (isPessimisticLockNeeded(document, user, canEdit)) {
            PessimisticLock pessimisticLock = createNewPessimisticLock(document, user);
            document.addPessimisticLock(pessimisticLock);
        }

        // find if any pessimistic locks are owned by the given user
        for (PessimisticLock pessimisticLock : document.getPessimisticLocks()) {
            if (pessimisticLock.isOwnedByUser(user)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determines whether to add a pessimistic lock on the {@code document} for {@code user} based on the current
     * pessimistic locks and edit modes.
     *
     * @param document the document on which the locks will be established
     * @param user the user for which the locks are being established
     * @param canEdit whether the user currently can edit the document
     *
     * @return true if a pessimistic lock should be added, false otherwise
     */
    protected boolean isPessimisticLockNeeded(Document document, Person user, boolean canEdit) {
        List<String> userOwnedLockDescriptors = new ArrayList<String>();
        Map<String, Set<String>> otherOwnedLockDescriptors = new HashMap<String,Set<String>>();

        // create the two lock containers that help determine whether to add a pessimistic lock
        for (PessimisticLock pessimisticLock : document.getPessimisticLocks()) {
            if (pessimisticLock.isOwnedByUser(user)) {
                userOwnedLockDescriptors.add(pessimisticLock.getLockDescriptor());
            } else {
                if (!otherOwnedLockDescriptors.containsKey(pessimisticLock.getLockDescriptor())) {
                    otherOwnedLockDescriptors.put(pessimisticLock.getLockDescriptor(), new HashSet<String>());
                }

                String otherOwnerPrincipalId = pessimisticLock.getOwnedByUser().getPrincipalId();
                otherOwnedLockDescriptors.get(pessimisticLock.getLockDescriptor()).add(otherOwnerPrincipalId);
            }
        }

        // double check whether the existing pessimistic locks are sane
        checkExistingPessimisticLocks(document, userOwnedLockDescriptors, otherOwnedLockDescriptors);

        // if no one has a lock on this document, then the document can be locked if the user can edit it
        if (userOwnedLockDescriptors.isEmpty() && otherOwnedLockDescriptors.isEmpty()) {
            return canEdit;
        }

        // if custom locking is turned on, then if the current user doesn't have a custom lock on this document and no
        // one else has a custom lock on this document, then the document can be locked if the user can edit it
        if (document.useCustomLockDescriptors()) {
            String customLockDescriptor = document.getCustomLockDescriptor(user);
            boolean userOwnsCustomLockDescriptor = userOwnedLockDescriptors.contains(customLockDescriptor);
            boolean otherOwnsCustomLockDescriptor = otherOwnedLockDescriptors.containsKey(customLockDescriptor);

            if (!userOwnsCustomLockDescriptor && !otherOwnsCustomLockDescriptor) {
                return canEdit;
            }
        }

        return false;
    }

    /**
     * Check to make sure that the current user doesn't erroneously share the same lock with another user.
     *
     * @param document the document to check for erroneous locks
     * @param userOwnedLockDescriptors the list of lock descriptors for the current user
     * @param otherOwnedLockDescriptors the map of other lock descriptors to all of the other users that own them
     */
    protected void checkExistingPessimisticLocks(Document document, List<String> userOwnedLockDescriptors, Map<String, Set<String>> otherOwnedLockDescriptors) {
        for (String userOwnedLockDescriptor : userOwnedLockDescriptors) {
            if (otherOwnedLockDescriptors.containsKey(userOwnedLockDescriptor)) {
                Set<String> otherOwnerPrincipalIds = otherOwnedLockDescriptors.get(userOwnedLockDescriptor);
                String workflowOwnerPrincipalId = getWorkflowPessimisticLockOwnerUser().getPrincipalId();
                boolean hasOtherOwners = !otherOwnerPrincipalIds.isEmpty();
                boolean hasMoreThanOneOtherOwner = otherOwnerPrincipalIds.size() > 1;
                boolean hasWorkflowOwner = otherOwnerPrincipalIds.contains(workflowOwnerPrincipalId);

                // if the lock has other owners, then if there is more than one other owner or if the single other owner
                // is not the workflow user, then throw an error
                if (hasOtherOwners && (hasMoreThanOneOtherOwner || !hasWorkflowOwner)) {
                    StringBuilder builder = new StringBuilder("Found an invalid lock status on document number ");
                    builder.append(document.getDocumentNumber());
                    builder.append(" with current user and other user both having locks concurrently");

                    if (document.useCustomLockDescriptors()) {
                        builder.append(" for custom lock descriptor '");
                        builder.append(userOwnedLockDescriptor);
                        builder.append("'");
                    }

                    builder.append(".");

                    LOG.debug(builder.toString());

                    throw new PessimisticLockingException(builder.toString());
                }
            }
        }
    }

    /**
     * Creates a new {@link PessimisticLock} on a {@code document} for a {@code user}.
     *
     * <p>
     * If the {@code document} uses custom lock descriptors, then the new lock is generated with a custom lock
     * descriptor for a portion of the document.  Otherwise, it will create a lock over the entire document.
     * </p>
     *
     * @param document the document on which the locks will be established
     * @param user the user for which the locks are being established
     *
     * @return the newly created lock object
     */
    protected PessimisticLock createNewPessimisticLock(Document document, Person user) {
        if (document.useCustomLockDescriptors()) {
            return generateNewLock(document.getDocumentNumber(), document.getCustomLockDescriptor(user), user);
        } else {
            return generateNewLock(document.getDocumentNumber(), user);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
	public Set getDocumentActions(Document document, Person user, Set<String> documentActions){
    	if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_CANCEL) && !hasPreRouteEditAuthorization(document, user) ){
    		documentActions.remove(KRADConstants.KUALI_ACTION_CAN_CANCEL);
    	}
    	if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_SAVE)  && !hasPreRouteEditAuthorization(document, user)){
    		documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SAVE);
    	}
        if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_ROUTE) && !hasPreRouteEditAuthorization(document, user)){
        	documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ROUTE);
        }
        if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE) && !hasPreRouteEditAuthorization(document, user)){
        	documentActions.remove(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE);
        }
    	return documentActions;
    }


    /**
     * This method checks to see that the given user has a lock on the document and return true if one is found.
     *
     * @param document - document to check
     * @param user - current user
     * @return true if the document is using Pessimistic Locking, the user has initiate authorization (see
     *         {@link #hasInitiateAuthorization(Document, Person)}), and the document has a lock owned by the given
     *         user. If the document is not using Pessimistic Locking the value returned will be that returned by
     *         {@link #hasInitiateAuthorization(Document, Person)}.
     *
     * @deprecated Different mechanism of obtaining authorization in KRAD
     */
    @Deprecated
    protected boolean hasPreRouteEditAuthorization(Document document, Person user) {
    	if (document.getPessimisticLocks().isEmpty()) {
    		return true;
    	}
    	for (Iterator<PessimisticLock> iterator = document.getPessimisticLocks().iterator(); iterator.hasNext();) {
    		PessimisticLock lock = iterator.next();
    		if (lock.isOwnedByUser(user)) {
    			return true;
            }
        }
        return false;
    }

    @Deprecated
    protected boolean usesPessimisticLocking(Document document) {
        return getDataDictionaryService().getDataDictionary().getDocumentEntry(document.getClass().getName()).getUsePessimisticLocking();
    }


    /**
     * This method creates a new {@link PessimisticLock} when Workflow processing requires one
     *
     * @param document - the document to create the lock against and add the lock to
     * @see org.kuali.rice.kns.document.authorization.DocumentAuthorizer#establishWorkflowPessimisticLocking(org.kuali.rice.krad.document.Document)
     */
    @Override
	public void establishWorkflowPessimisticLocking(Document document) {
        PessimisticLock lock = createNewPessimisticLock(document, getWorkflowPessimisticLockOwnerUser());
        document.addPessimisticLock(lock);
    }

    /**
     * This method releases locks created via the {@link #establishWorkflowPessimisticLocking(Document)} method for the given document
     *
     * @param document - document to release locks from
     * @see org.kuali.rice.kns.document.authorization.DocumentAuthorizer#releaseWorkflowPessimisticLocking(org.kuali.rice.krad.document.Document)
     */
    @Override
	public void releaseWorkflowPessimisticLocking(Document document) {
        releaseAllLocksForUser(document.getPessimisticLocks(), getWorkflowPessimisticLockOwnerUser());
        document.refreshPessimisticLocks();
    }

    /**
     * This method identifies the user that should be used to create and clear {@link PessimisticLock} objects required by
     * Workflow.<br>
     * <br>
     * The default is the Kuali system user defined by {@link RiceConstants#SYSTEM_USER}. This method can be overriden by
     * implementing documents if another user is needed.
     *
     * @return a valid {@link Person} object
     */
    protected Person getWorkflowPessimisticLockOwnerUser() {
        String networkId = KRADConstants.SYSTEM_USER;
        return getPersonService().getPersonByPrincipalName(networkId);
    }

    /**
     * {@inheritDoc}
     *
     * This implementation will check the given document, editMode map, and user object to verify Pessimistic Locking. If the
     * given edit mode map contains an 'entry type' edit mode then the system will check the locks already in existence on
     * the document. If a valid lock for the given user is found the system will return the given edit mode map. If a valid
     * lock is found but is owned by another user the edit mode map returned will have any 'entry type' edit modes removed. If the
     * given document has no locks and the edit mode map passed in has at least one 'entry type' mode then a new
     * {@link PessimisticLock} object will be created and set on the document for the given user.<br>
     * <br>
     * NOTE: This method is only called if the document uses pessimistic locking as described in the data dictionary file.
     *
     * @see org.kuali.rice.kns.document.authorization.DocumentAuthorizer#establishLocks(org.kuali.rice.krad.document.Document,
     *      java.util.Map, org.kuali.rice.kim.api.identity.Person)
     */
    @Override
    @Deprecated
	public Map establishLocks(Document document, Map editMode, Person user) {
        Map editModeMap = new HashMap();
        // givenUserLockDescriptors is a list of lock descriptors currently held on the document by the given user
        List<String> givenUserLockDescriptors = new ArrayList<String>();
        // lockDescriptorUsers is a map with lock descriptors as keys and users other than the given user who hold a lock of each descriptor
        Map<String,Set<Person>> lockDescriptorUsers = new HashMap<String,Set<Person>>();

        // build the givenUserLockDescriptors set and the lockDescriptorUsers map
        for (PessimisticLock lock : document.getPessimisticLocks()) {
            if (lock.isOwnedByUser(user)) {
                // lock is owned by given user
                givenUserLockDescriptors.add(lock.getLockDescriptor());
            } else {
                // lock is not owned by the given user
                if (!lockDescriptorUsers.containsKey(lock.getLockDescriptor())) {
                    lockDescriptorUsers.put(lock.getLockDescriptor(), new HashSet<Person>());
                }
                lockDescriptorUsers.get(lock.getLockDescriptor()).add(lock.getOwnedByUser());
            }
        }

        // verify that no locks held by current user exist for any other user
        for (String givenUserLockDescriptor : givenUserLockDescriptors) {
            if ( (lockDescriptorUsers.containsKey(givenUserLockDescriptor)) && (lockDescriptorUsers.get(givenUserLockDescriptor).size() > 0) ) {
                Set<Person> users = lockDescriptorUsers.get(givenUserLockDescriptor);
                if ( (users.size() != 1) || (!getWorkflowPessimisticLockOwnerUser().getPrincipalId().equals(users.iterator().next().getPrincipalId())) ) {
                    String descriptorText = (document.useCustomLockDescriptors()) ? " using lock descriptor '" + givenUserLockDescriptor + "'" : "";
                    String errorMsg = "Found an invalid lock status on document number " + document.getDocumentNumber() + "with current user and other user both having locks" + descriptorText + " concurrently";
                    LOG.debug(errorMsg);
                    throw new PessimisticLockingException(errorMsg);
                }
            }
        }

        // check to see if the given user has any locks in the system at all
        if (givenUserLockDescriptors.isEmpty()) {
            // given user has no locks... check for other user locks
            if (lockDescriptorUsers.isEmpty()) {
                // no other user has any locks... set up locks for given user if user has edit privileges
                if (isLockRequiredByUser(document, editMode, user)) {
                    document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
                }
                editModeMap.putAll(editMode);
            } else {
                // at least one other user has at least one other lock... adjust edit mode for read only
                if (document.useCustomLockDescriptors()) {
                    // check to see if the custom lock descriptor is already in use
                    String customLockDescriptor = document.getCustomLockDescriptor(user);
                    if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
                        // at least one other user has this descriptor locked... remove editable edit modes
                        editModeMap = getEditModeWithEditableModesRemoved(editMode);
                    } else {
                        // no other user has a lock with this descriptor
                        if (isLockRequiredByUser(document, editMode, user)) {
                            document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
                        }
                        editModeMap.putAll(editMode);
                    }
                } else {
                    editModeMap = getEditModeWithEditableModesRemoved(editMode);
                }
            }
        } else {
            // given user already has at least one lock descriptor
            if (document.useCustomLockDescriptors()) {
                // get the custom lock descriptor and check to see if if the given user has a lock with that descriptor
                String customLockDescriptor = document.getCustomLockDescriptor(user);
                if (givenUserLockDescriptors.contains(customLockDescriptor)) {
                    // user already has lock that is required
                    editModeMap.putAll(editMode);
                } else {
                    // user does not have lock for descriptor required
                    if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
                        // another user has the lock descriptor that the given user requires... disallow lock and alter edit modes to have read only
                        editModeMap = getEditModeWithEditableModesRemoved(editMode);
                    } else {
                        // no other user has a lock with this descriptor... check if this user needs a lock
                        if (isLockRequiredByUser(document, editMode, user)) {
                            document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
                        }
                        editModeMap.putAll(editMode);
                    }
                }
            } else {
                // user already has lock and no descriptors are being used... use the existing edit modes
                editModeMap.putAll(editMode);
            }
        }

        return editModeMap;
    }

    /**
     * This method is used to check if the given parameters warrant a new lock to be created for the given user. This method
     * utilizes the {@link #isEntryEditMode(java.util.Map.Entry)} method.
     *
     * @param document -
     *            document to verify lock creation against
     * @param editMode -
     *            edit modes list to check for 'entry type' edit modes
     * @param user -
     *            user the lock will be 'owned' by
     * @return true if the given edit mode map has at least one 'entry type' edit mode... false otherwise
     */
    @Deprecated
    protected boolean isLockRequiredByUser(Document document, Map editMode, Person user) {
        // check for entry edit mode
        for (Iterator iterator = editMode.entrySet().iterator(); iterator.hasNext();) {
            Map.Entry entry = (Map.Entry) iterator.next();
            if (isEntryEditMode(entry)) {
                return true;
            }
        }
        return false;
    }

   /**
     * This method is used to remove edit modes from the given map that allow the user to edit data on the document. This
     * method utilizes the {@link #isEntryEditMode(java.util.Map.Entry)} method to identify if an edit mode is defined as an
     * 'entry type' edit mode. It also uses the {@link #getEntryEditModeReplacementMode(java.util.Map.Entry)} method to replace
     * any 'entry type' edit modes it finds.
     *
     * @param currentEditMode -
     *            current set of edit modes the user has assigned to them
     * @return an adjusted edit mode map where 'entry type' edit modes have been removed or replaced using the
     *         {@link #getEntryEditModeReplacementMode} method
     */
    @Deprecated
    protected Map getEditModeWithEditableModesRemoved(Map currentEditMode) {
        Map editModeMap = new HashMap();
        for (Iterator iterator = currentEditMode.entrySet().iterator(); iterator.hasNext();) {
            Map.Entry entry = (Map.Entry) iterator.next();
            if (isEntryEditMode(entry)) {
                editModeMap.putAll(getEntryEditModeReplacementMode(entry));
            } else {
                editModeMap.put(entry.getKey(), entry.getValue());
            }
        }
        return editModeMap;
    }

    /**
     * This method is used to check if the given {@link Map.Entry} is an 'entry type' edit mode and that the value is set to
     * signify that this user has that edit mode available to them
     *
     * @param entry -
     *            the {@link Map.Entry} object that contains an edit mode such as the ones returned but
     *            {@link #getEditMode(Document, Person)}
     * @return true if the given entry has a key signifying an 'entry type' edit mode and the value is equal to
     *         {@link #EDIT_MODE_DEFAULT_TRUE_VALUE}... false if not
     */
    @Deprecated
    protected boolean isEntryEditMode(Map.Entry entry) {
    	// check for FULL_ENTRY edit mode set to default true value
    	if (AuthorizationConstants.EditMode.FULL_ENTRY.equals(entry.getKey())) {
    		String fullEntryEditModeValue = (String)entry.getValue();
    		return ( StringUtils.equalsIgnoreCase(KRADConstants.KUALI_DEFAULT_TRUE_VALUE, fullEntryEditModeValue) );
    	}
    	return false;
    }

    /**
     * This method is used to return values needed to replace the given 'entry type' edit mode {@link Map.Entry} with one that will not allow the user to enter data on the document
     *
     * @param entry - the current 'entry type' edit mode to replace
     * @return a Map of edit modes that will be used to replace this edit mode (represented by the given entry parameter)
     */
    protected Map getEntryEditModeReplacementMode(Map.Entry entry) {
        Map editMode = new HashMap();
        editMode.put(AuthorizationConstants.EditMode.VIEW_ONLY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
        return editMode;
    }

    /**
     * This method creates a new {@link PessimisticLock} object using the given document and user. If the document's
     * useCustomLockDescriptors() method returns true then the new lock will also have a custom lock descriptor
     * value set to the return value of the document's getCustomLockDescriptor(Person) method.
     *
     * @param document -
     *            document to place the lock on
     * @param editMode -
     *            current edit modes for given user
     * @param user -
     *            user who will 'own' the new lock object
     * @return the newly created lock object
     */
    @Deprecated
    protected PessimisticLock createNewPessimisticLock(Document document, Map editMode, Person user) {
        if (document.useCustomLockDescriptors()) {
            return generateNewLock(document.getDocumentNumber(), document.getCustomLockDescriptor(user), user);
        } else {
            return generateNewLock(document.getDocumentNumber(), user);
        }
    }

    public PersonService getPersonService() {
        if ( personService == null ) {
            personService = KimApiServiceLocator.getPersonService();
        }
        return personService;
    }

	public DataDictionaryService getDataDictionaryService() {
		return dataDictionaryService;
	}

	@Required
	public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
		this.dataDictionaryService = dataDictionaryService;
	}

	public PermissionService getPermissionService() {
        if ( permissionService == null ) {
        	permissionService = KimApiServiceLocator.getPermissionService();
        }
		return permissionService;
	}


	@Required
    public void setDataObjectService(DataObjectService dataObjectService) {
		this.dataObjectService = dataObjectService;
	}
}

