/* bcwti
 *
 * Copyright (c) 2008 Parametric Technology Corporation (PTC). All Rights Reserved.
 *
 * This software is the confidential and proprietary information of PTC
 * and is subject to the terms of a software license agreement. You shall
 * not disclose such confidential information and shall use it only in accordance
 * with the terms of the license agreement.
 *
 * ecwti
 */
package com.ptc.windchill.enterprise.wip;

import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import org.apache.log4j.Logger;

import wt.access.AccessControlHelper;
import wt.access.AccessPermission;
import wt.content.ContentHelper;
import wt.content.ContentItem;
import wt.content.ExternalStoredData;
import wt.content.FormatContentHolder;
import wt.content.URLData;
import wt.fc.ReferenceFactory;
import wt.fc.WTReference;
import wt.folder.CabinetBased;
import wt.folder.Folder;
import wt.folder.FolderEntry;
import wt.folder.FolderHelper;
import wt.inf.container.WTContained;
import wt.inf.container.WTContainerHelper;
import wt.inf.container.WTContainerRef;
import wt.log4j.LogR;
import wt.org.WTPrincipal;
import wt.org.WTUser;
import wt.preference.PreferenceClient;
import wt.preference.PreferenceHelper;
import wt.sandbox.SandboxHelper;
import wt.session.SessionHelper;
import wt.util.WTContext;
import wt.util.WTException;
import wt.util.WTInvalidParameterException;
import wt.vc.Iterated;
import wt.vc.VersionControlHelper;
import wt.vc.wip.WorkInProgressHelper;
import wt.vc.wip.Workable;

import com.ptc.core.ui.resources.FeedbackType;
import com.ptc.core.ui.validation.DefaultUIComponentValidator;
import com.ptc.core.ui.validation.UIComponentValidator;
import com.ptc.core.ui.validation.UIValidationCriteria;
import com.ptc.core.ui.validation.UIValidationFeedbackMsg;
import com.ptc.core.ui.validation.UIValidationKey;
import com.ptc.core.ui.validation.UIValidationResult;
import com.ptc.core.ui.validation.UIValidationResultSet;
import com.ptc.core.ui.validation.UIValidationStatus;

public class DefaultWIPValidator extends DefaultUIComponentValidator {
    private Logger logger = LogR.getLogger(DefaultWIPValidator.class.getName());
    
    private ReferenceFactory refFactory = null;

    private static final String WIP_VALIDATION_RESOURCE = "com.ptc.windchill.enterprise.wip.WIPResource";
    
    public static final String FULL = "full";

    public static final String LIMITED = "limited";

    public static final String SELECTED = "selected";

    public static final String FORMSUBMISSION = "formSubmission";

    protected final static String CHECKOUT_AND_EDIT = "checkoutAndEdit";

    protected final static String CHECKIN = "checkin";

    protected final static String CHECKOUT = "checkout";

    protected final static String CHECKOUT_AND_DOWNLOAD = "checkout_and_download";

    protected final static String EDIT = "edit";

    protected final static String REPLACE_CONTENT = "replace_content";

    protected final static String UNDOCHECKOUT = "undocheckout";

    protected enum accessPermissions {
        MODIFY, MODIFY_CONTENT, DOWNLOAD;

        // Do arithmetic op represented by this constant
        AccessPermission getAccessPermission() {
            switch (this) {
            case MODIFY:
                return AccessPermission.MODIFY;
            case MODIFY_CONTENT:
                return AccessPermission.MODIFY_CONTENT;
            case DOWNLOAD:
                return AccessPermission.DOWNLOAD;
            }
            throw new AssertionError("Unknown accessPermission: " + this);
        }
    }

    protected class wipValidationResult {
        private boolean success = false;

        private List messages = null;

        private boolean hide = false;

        private boolean confirm = false;

        public void setConfirm(boolean confirm) {
            this.confirm = confirm;
        }

        public boolean isConfirm() {
            return confirm;
        }

        public List getMessage() {
            return messages;
        }

        public boolean isHide() {
            return hide;
        }

        public boolean isSuccess() {
            return success;
        }

        public void addMessage(UIValidationFeedbackMsg msg) {
            if (messages == null) {
                messages = new ArrayList();
            }
            messages.add(msg);
        }

        public wipValidationResult(boolean success) {
            this.success = success;
        }

        public wipValidationResult(boolean success, boolean hide) {
            this.success = success;
            this.hide = hide;
        }

        public UIValidationResult getValidationResult(
                UIValidationKey validation_key, WTReference wt_ref,
                String validation_type) {

            UIValidationStatus status;
            if (confirm) {
                status = UIValidationStatus.PROMPT_FOR_CONFIRMATION;
            }
            else if (success) {
                if (validation_type.equals(FULL)) {
                    status = UIValidationStatus.ENABLED;
                } else {
                    status = UIValidationStatus.PERMITTED;
                }
            } else {
                if (validation_type.equals(FULL)) {
                    if (hide) {
                        status = UIValidationStatus.HIDDEN;
                    } else {
                        status = UIValidationStatus.DISABLED;
                    }
                } else {
                    status = UIValidationStatus.DENIED;
                }
            }
            return new UIValidationResult(validation_key, status, wt_ref,
                    messages);
        }

    }

    /**
     * This implementation of performLimitedPreValdiation will check get
     * checkout state of all the Workable objects in the validation_criteria's
     * targetObjects WTCollection, and base its validation results on whether an
     * object in the given state can have the specified action performed on it.
     * (e.g., an object in the checked-in state can not have an undo checkout
     * action performed on it)
     *
     * At a minimum, a caller of this method should provide the targetObjects
     * WTCollection in the validation_criteria argument.
     *
     * The expected validation_key arguments for this method are: checkin
     * checkout undocheckout checkout_and_edit edit replace_content
     *
     * <BR>
     * <BR>
     * <B>Supported API: </B>false
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param locale
     *            The user's Locale. If a <i>null</i> value is passed in, the
     *            session locale will be used.
     * @return UIValidationResultSet
     */
    @Override
    public UIValidationResultSet performLimitedPreValidation(
            UIValidationKey validation_key,
            UIValidationCriteria validation_criteria, Locale locale) {
        UIValidationResultSet result_set = null;
        logger
                .debug("ENTERING DefaultWIPValidator.performLimitedPreValidation");
        logger.trace("  validtionKey -> " + validation_key);
        logger.trace("  validation_criteria -> "
                + validation_criteria.toString());
        try {
            result_set = performWIPValidation(validation_key,
                    validation_criteria, locale, LIMITED, validation_key
                            .getComponentID().toString());
        } catch (WTException e) {
            e.printStackTrace();
        }
        logger.trace("RETURNING " + result_set.toString());
        logger.debug("EXITING DefaultWIPValidator.performLimitedPreValidation");
        return result_set;
    }

    /**
     * performFullPreValidation is called whenever a validation of an action is
     * called for before it is displayed to the user. This method will be called
     * everytime the user clicks on the top level Actions Menu in details page
     * or row level one in Folders and Home page. The method will return a
     * resultset with resultset set to be DISABLED or ENABLED.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param locale
     *            The user's Locale. If a <i>null</i> value is passed in, the
     *            session locale will be used.
     * @return UIValidationResultSet
     */
    @Override
    public UIValidationResultSet performFullPreValidation(
            UIValidationKey validation_key,
            UIValidationCriteria validation_criteria, Locale locale) {
        UIValidationResultSet result_set = null;
        logger
                .debug("ENTERING DefaultWIPValidator.performFullPreValidation zzz");
        logger.trace("  validtionKey -> " + validation_key);
        logger.trace("  validation_criteria -> "
                + validation_criteria.toString());

        try {
            // Checks for Work Package related validation
            UIComponentValidator wpValidator = getSubValidator("WPValidation");
            if (wpValidator != null) {
                result_set = wpValidator.performFullPreValidation(
                        validation_key, validation_criteria, locale);
                if (!result_set.getAllResults().isEmpty()) {
                    return result_set;
                }
            }// Work package related validation ends

            result_set = performWIPValidation(validation_key,
                    validation_criteria, locale, FULL, validation_key
                            .getComponentID().toString());
        } catch (WTException e) {
            e.printStackTrace();
        }

        logger.trace("RETURNING " + result_set.toString());
        logger.debug("EXITING DefaultWIPValidator.performFullPreValidation");
        return result_set;
    }

    /**
     * validateSelectedAction is called whenever a validation of an action is
     * called for after it is clicked by the user. This method will be called
     * everytime the user clicks the action popped up on the top level Actions
     * Menu in details page or row level one in Folders and Home page. The
     * method will return a PERMITTED/DENIED status in the resultset.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param locale
     *            The user's Locale. If a <i>null</i> value is passed in, the
     *            session locale will be used.
     * @return UIValidationResult
     */
    @Override
    public UIValidationResult validateSelectedAction(
            UIValidationKey validation_key,
            UIValidationCriteria validation_criteria, Locale locale) {
        logger.debug("ENTERING DefaultWIPValidator.validateSelectedAction");
        logger.trace("  validtionKey -> " + validation_key);
        logger.trace("  validation_criteria -> "
                + validation_criteria.toString());

        UIValidationResultSet result_set = null;

        try {

            result_set = performWIPValidation(validation_key,
                    validation_criteria, locale, SELECTED, validation_key
                            .getComponentID().toString());
        } catch (WTException e) {
            e.printStackTrace();
        }

        logger.trace("RETURNING " + result_set.toString());
        logger.debug("EXITING DefaultWIPValidator.performFullPreValidation");
        UIValidationResult result = result_set.getAllResults().get(0);

        logger.trace("RETURNING " + result.toString());
        logger.debug("EXITING DefaultWIPValidator.validateSelectedAction");

        return result;
    }

    @Override
    public UIValidationResultSet validateSelectedMultiSelectAction(
            UIValidationKey validation_key,
            UIValidationCriteria validation_criteria, Locale locale) {
        UIValidationResultSet result_set = null;
        logger
                .debug("ENTERING DefaultWIPValidator.validateSelectedMultiSelectAction");
        logger.trace("  validtionKey -> " + validation_key);
        logger.trace("  validation_criteria -> "
                + validation_criteria.toString());
        try {
            result_set = performWIPValidation(validation_key,
                    validation_criteria, locale, SELECTED, validation_key
                            .getComponentID().toString());
        } catch (WTException e) {
            e.printStackTrace();
        }
        logger.trace("RETURNING " + result_set.toString());
        logger
                .debug("EXITING DefaultWIPValidator.validateSelectedMultiSelectAction");
        return result_set;
    }

    /**
     * performWIPValidation method will check for the specific action called
     * upon and then decide which of the particular validator needs to be
     * called.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param locale
     *            The user's Locale. If a <i>null</i> value is passed in, the
     *            session locale will be used.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     * @return UIValidationResult
     */
    protected UIValidationResultSet performWIPValidation(
            UIValidationKey validation_key,
            UIValidationCriteria validation_criteria, Locale locale,
            String validation_type, String validationAction) throws WTException {
        UIValidationResultSet result_set = new UIValidationResultSet();
        WTReference wr = validation_criteria.getContextObject();
        WTUser user = (WTUser) validation_criteria.getUser().getPrincipal();
        if (user == null) {
            user = (WTUser) SessionHelper.manager.getPrincipal();
        }
        Workable workable = null;
        if (Workable.class.isAssignableFrom(wr.getReferencedClass())) {
            workable = (Workable) wr.getObject();
        }

        if (user == null) {
            throw new WTInvalidParameterException(
                    "No user passed into the DefaultWIPValidator");
        }
        if (workable == null) {
            throw new WTInvalidParameterException(
                    "No workable passed into the DefaultWIPValidator");
        }
        if (validationAction == null) {
            throw new WTInvalidParameterException(
                    "No action passed into the DefaultWIPValidator");
        }
        logger.debug("user:" + user.toString());
        logger.debug("workable:" + workable.toString());
        logger.debug("action:" + validationAction.toString());

        if (validationAction.equals(CHECKIN)) {
            logger.debug("CHECKIN validation");
            wipValidationResult result = performCheckinValidation(
                    validation_key, workable, validation_criteria,
                    validation_type, user);
            logger.debug("CHECKIN validation type:" + validation_type
                    + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        } else if (validationAction.equals(CHECKOUT)) {
            logger.debug("CHECKOUT validation");
            wipValidationResult result = performCheckoutValidation(
                    validation_key, workable, validation_criteria,
                    validation_type, user);
            logger.debug("CHECKOUT validation type:" + validation_type
                    + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        } else if (validationAction.equals(CHECKOUT_AND_EDIT)) {
            logger.debug("CHECKOUT_AND_EDIT validation");
            wipValidationResult result = performCheckoutAndEditValidation(
                    validation_key, workable, validation_criteria,
                    validation_type, user);
            logger.debug("CHECKOUT_AND_EDIT validation type:" + validation_type
                    + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        } else if (validationAction.equals(EDIT)) {
            logger.debug("EDIT validation");
            wipValidationResult result = performEditValidation(workable, user);
            logger.debug("EDIT validation type:" + validation_type
                    + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        } else if (validationAction.equals(UNDOCHECKOUT)) {
            logger.debug("UNDOCHECKOUT validation");
            wipValidationResult result = performUndoCheckoutValidation(
                    validation_key, workable, validation_criteria,
                    validation_type, user);
            logger.debug("UNDOCHECKOUT validation type:" + validation_type
                    + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        } else if (validationAction.equals(REPLACE_CONTENT)) {
            logger.debug("REPLACE_CONTENT validation");
            wipValidationResult result = performReplaceContentValidation(
                    validation_key, workable, validation_criteria,
                    validation_type, user);
            logger.debug("REPLACE_CONTENT validation type:" + validation_type
                    + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        } else if (validationAction.equals(CHECKOUT_AND_DOWNLOAD)) {
            logger.debug("CHECKOUT_AND_DOWNLOAD validation");
            wipValidationResult result = performCheckoutAndDownloadValidation(
                    validation_key, workable, validation_criteria,
                    validation_type, user);
            logger.debug("CHECKOUT_AND_DOWNLOAD validation type:"
                    + validation_type + " enabled:" + result.isSuccess());
            result_set.addResult(result.getValidationResult(validation_key, wr,
                    validation_type));
        }
        return result_set;
    }

    /**
     *
     * performEditValidation method will called upon doing the Edit action
     * validation. This validator logic allows or enables the action on the
     * workables for a user which satisifes the following conditions.
     *  - The user has MODIFY permission set for this workable. - The workable
     * is checked out. - and checked-out to the current user.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param workable
     *            Object on which the action is going to operate.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performEditValidation(Workable workable,
            WTPrincipal userP) throws WTException {

        boolean modifyAccess = hasAccess(userP, workable,
                accessPermissions.MODIFY);
        logger.debug("modifyAccess:" + modifyAccess);
        if (!modifyAccess)
            return new wipValidationResult(false, true);

        /*
         * If Edit is allowed on Checkin Objects then check that the object is
         * not checked out by anyone else.
         */
        boolean checkedOutToUser = isCheckedOutToUser(workable, userP);
        logger.debug("checkedOutToUser:" + checkedOutToUser);
        if (checkedOutToUser)
            return new wipValidationResult(true);

        return new wipValidationResult(false);
    }

    /**
     * performCheckoutAndEditValidation method will called upon doing the
     * CheckoutAndEdit action validation. This validator logic allows or enables
     * the action on the workables for a particular user which is satisifes the
     * following conditions. - The preference EDIT_ACTION_ON_CHECKED_IN is set. -
     * The user and workable passes the Checkout validation.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param workable
     *            Object on which the action is going to operate.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     *
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performCheckoutAndEditValidation(
            UIValidationKey validation_key, Workable workable,
            UIValidationCriteria validation_criteria, String validation_type,
            WTUser user) throws WTException {

        /**
         * Display of the CO and Edit action is controlled by the preference
         * EDIT_ACTION_ON_CHECKED_IN. If this is true means show the Checkout
         * And Edit action on checked in objects. This is the only way an CO and
         * Edit validation will differ from CO.
         */

        boolean allowEditOnCheckin = (Boolean) getPreferenceService().getValue(
                validation_criteria.getParentContainer(),
                "EDIT_ACTION_ON_CHECKED_IN",
                PreferenceClient.WINDCHILL_CLIENT_NAME,
                user);
        logger.debug("allowEditOnCheckin:" + allowEditOnCheckin);
        if (!allowEditOnCheckin) {
            return new wipValidationResult(false, true);
        }

        /**
         * Now check for the checkout validation.
         */
        return performCheckoutValidation(validation_key, workable,
                validation_criteria, validation_type, user);
    }

    /**
     * performUndoCheckoutValidation method will called upon doing the
     * UndoCheckout action validation. This validator logic allows or enables
     * the action on the workables for a particular user which is checked-out by
     * that user or if the user is administrator. It will also not be displayed
     * if the workable is PDMChecked out.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performUndoCheckoutValidation(   
            UIValidationKey validation_key, Workable workable,
            UIValidationCriteria validation_criteria, String validation_type,
            WTUser user) throws WTException {

        if (workable == null)
            return new wipValidationResult(false);

        // WTUser user = (WTUser)validation_criteria.getUser().getPrincipal();

        /*
         * Check if the object is locked by the sandbox checkout. Locked object
         * does not have the permission for UndoCheckout.
         */

        boolean chkoutToSand = isCheckedOutToSandbox(workable);
        logger.debug("chkoutToSand:" + chkoutToSand);
        if (chkoutToSand)
            return new wipValidationResult(false);

        boolean checkedOut = isCheckedOut(workable);
        logger.debug("checkedOut:" + checkedOut);
        if (!checkedOut) {
            return new wipValidationResult(false);
        }

        /*
         * If the working copy is not checked out to current user or the current
         * user is not the administrator of its container then disable it. I use
         * an if else here so that I can both print debugging information, but
         * also so that I can prevent the server hit to determine if the user is
         * an administrator if possible.
         */
        boolean checkedOutToUser = isCheckedOutToUser(workable, user);
        logger.debug("checkedOutToUser:" + checkedOutToUser);
        if (checkedOutToUser) {
            return new wipValidationResult(true);
        } else {
            boolean userIsAdmin = getWTContainerService().isAdministrator(  
                    validation_criteria.getParentContainer(), user);
            logger.debug("userIsAdmin:" + userIsAdmin);
            if (userIsAdmin) {
                return new wipValidationResult(true);
            }
        }
        
        /* If the object is a working copy & ADMINISTRATIVE permission is lacking, 
         * then allow undo-checkout only if DELETE and MODIFY is allowed
         * else 
         * If the object is original copy and has ADMINISTRATIVE permissions, only then undo-checkout is allowed. 
        */

        if (WorkInProgressHelper.isWorkingCopy(workable))
        {
        	if ((AccessControlHelper.manager.hasAccess(workable, AccessPermission.ADMINISTRATIVE)))
        		return new wipValidationResult(true);
        	else
        	{
        		Folder workingFolder = FolderHelper.getFolder((FolderEntry)workable);
        		boolean userHasDeleteOnWorking = AccessControlHelper.manager.hasAccess(workable, AccessPermission.DELETE);
        		boolean userHasModifyOnFolder = (workingFolder == null) ? true : AccessControlHelper.manager.hasAccess(workingFolder, AccessPermission.MODIFY);

        		if (userHasDeleteOnWorking && userHasModifyOnFolder)
        			return new wipValidationResult(true);
        	}
        }
        else
        	if (WorkInProgressHelper.isCheckedOut(workable))
        	{
        		if (AccessControlHelper.manager.hasAccess(workable, AccessPermission.ADMINISTRATIVE))
        			return new wipValidationResult(true);
        	}
        return new wipValidationResult(false);
        
    }

    /**
     * performCheckinValidation method will called upon doing the Checkin action
     * validation. This validator logic allows or enables the action on the
     * workables for a particular user which is satisifes the following
     * conditions. - The workable is checked out and to the current user. - The
     * workable is not PDMChecked out.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     * @param wtuser
     *            temporary parameter until the deprecated public peer of this
     *            method is removed
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performCheckinValidation(
            UIValidationKey validation_key, Workable workable,
            UIValidationCriteria validation_criteria, String validation_type,
            WTUser user) throws WTException {
        if (workable == null)
            return new wipValidationResult(false);

        if (user == null)
            user = (WTUser) validation_criteria.getUser().getPrincipal();

        boolean checkedOutToSandbox = isCheckedOutToSandbox(workable);
        logger.debug("checkedOutToSandbox:" + checkedOutToSandbox);
        if (checkedOutToSandbox) {
            return new wipValidationResult(false);
        }
        if (validation_type.equals(LIMITED)) {
            logger.debug("doing limited validation");
            boolean checkedOut = isCheckedOut(workable);
            logger.debug("checkedOut:" + checkedOut);
            if (checkedOut == false) {
                return new wipValidationResult(false);
            }
            return new wipValidationResult(true);
        }

        else if (validation_type.equals(FULL)
                || validation_type.equals(SELECTED)
                || validation_type.equals(FORMSUBMISSION)) {

            if (workable instanceof CabinetBased) {
                CabinetBased orig;
                if (WorkInProgressHelper.isWorkingCopy(workable)) {
                    orig = (CabinetBased) WorkInProgressHelper.service
                            .originalCopyOf(workable);
                } else {
                    orig = (CabinetBased) workable;
                }
            }

            boolean checkedOutToUser = isCheckedOutToUser(workable, user);
            logger.debug("checkedOutToUser:" + checkedOutToUser);
            if (!checkedOutToUser)
                return new wipValidationResult(false);

            return new wipValidationResult(true);

        }
        // If the validation is not of Full/Selected or Limited just disable the
        // action
        return new wipValidationResult(false);
    }

    /**
     *
     * performCheckoutValidation method will called upon doing the Checkout
     * action validation. This validator logic allows or enables the action on
     * the workables for a particular user which is satisifes the following
     * conditions. - The workable is checked-in. - The user has MODIFY access. (
     * As of now the validation does not check for the access against the role
     * but just the user.) - The workable is the latest copy or if not the
     * preference for non-latest iteration checkout is set. - The workable is
     * not PDMChecked out.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param workable
     *            Object on which the action is going to operate.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     * @param wtuser
     *            temporary parameter until the deprecated public peer of this
     *            method is removed
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performCheckoutValidation(
            UIValidationKey validation_key, Workable workable,
            UIValidationCriteria validation_criteria, String validation_type,
            WTUser user) throws WTException {

        String feedbackMessage = "";
        ResourceBundle wipValidationRb = ResourceBundle.getBundle(
                WIP_VALIDATION_RESOURCE, WTContext.getContext().getLocale());

        if (workable == null)
            return new wipValidationResult(false);

        if (user == null)
            user = (WTUser) validation_criteria.getUser().getPrincipal();

        if (validation_type.equals(LIMITED)) {
            boolean checkedOut = isCheckedOut(workable);
            logger.debug("isCheckedOut:" + checkedOut);

            return new wipValidationResult(!checkedOut);
        } else if (validation_type.equals(FULL)
                || validation_type.equals(SELECTED)
                || validation_type.equals(FORMSUBMISSION)) {

            /*
             * Check if the object is locked by the sandbox checkout. Locked
             * object does not have the permission for Checkout.
             */
            boolean checkedOutToSandbox = SandboxHelper
                    .isCheckedOutToSandbox(workable);
            logger.debug("checkedOutToSandbox:" + checkedOutToSandbox);
            if (checkedOutToSandbox) {
                return new wipValidationResult(false);
            }

            boolean checkoutAllowed = isCheckoutAllowed(workable);
            logger.debug("checkoutAllowed:" + checkoutAllowed);
            if (!checkoutAllowed)
                return new wipValidationResult(false);

            // Check the access permisson against the user.
            boolean modifyAccess = hasAccess(user, workable,
                    accessPermissions.MODIFY);
            logger.debug("modifyAccess:" + modifyAccess);
            if (!modifyAccess) {
                feedbackMessage = wipValidationRb
                        .getString(WIPResource.CHECKOUT_WITHOUT_ACCESS);
                UIValidationFeedbackMsg message = new UIValidationFeedbackMsg(
                        feedbackMessage, (FeedbackType.ERROR));
                wipValidationResult result = new wipValidationResult(false, true);
                result.addMessage(message);
                return result;
            }
            boolean checkedOut = isCheckedOut(workable);
            logger.debug("checkedOut:" + checkedOut);
            if (checkedOut) {
                feedbackMessage = wipValidationRb
                        .getString(WIPResource.ALREADY_CHECKEDOUT);
                UIValidationFeedbackMsg message = new UIValidationFeedbackMsg(
                        feedbackMessage, (FeedbackType.ERROR));
                wipValidationResult result = new wipValidationResult(false);
                result.addMessage(message);
                return result;
            }

            /*
             * Check if its the latest iteration along with its preferance. If
             * not then allow checkout with a warning message.
             */
            boolean latestIteration = isLatestIteration((Iterated) workable);
            logger.debug("latestIteration:" + latestIteration);
            if (!latestIteration) {
                boolean allowNonLatestIterationCheckout = true;
                if (user instanceof WTUser) {
                    WTContainerRef containerRef = null;

                    if (validation_criteria != null)
                        containerRef = validation_criteria.getParentContainer();

                    if (containerRef == null && workable instanceof WTContained)
                        containerRef = ((WTContained) workable).getContainerReference();

                    logger.debug("containerRef : " + containerRef);

                    Object prefValue = getPreferenceService().getValue(
                            containerRef,
                            "/wt/vc/wip/DefaultType/wt.vc.wip.NonLatestCheckoutValidator",
                            PreferenceClient.WINDCHILL_CLIENT_NAME,
                            user);
                    if(prefValue!=null){
                        logger.debug("prefValue: "  +  prefValue.toString());
                        
                        // If the user doesn't rerun the preference load after migrating to X12, this preference 
                        // will still be a boolean, hence this Boolean/String check is included here.
                        // This could probably be removed at/after X20.
                        
                        if(prefValue instanceof Boolean)
                            allowNonLatestIterationCheckout=(Boolean)prefValue;
                        
                        if(prefValue instanceof String){
                            if(prefValue.equals("false"))
                                allowNonLatestIterationCheckout=false;
                        }
                    }
                    else
                        allowNonLatestIterationCheckout=false;
                    
                    logger.debug("allowNonLatestIterationCheckout:"
                            + allowNonLatestIterationCheckout);
                }

                if (allowNonLatestIterationCheckout) {
                    if (validation_type.equals(FORMSUBMISSION)) {
                        wipValidationResult result = new wipValidationResult(true);
                        result.setConfirm(true);
                        feedbackMessage = wipValidationRb
                        .getString(WIPResource.NON_LATEST_ITERATION);
                        UIValidationFeedbackMsg message = new UIValidationFeedbackMsg(
                                feedbackMessage, (FeedbackType.CONFIRMATION));
                        result.addMessage(message);
                        return result;
                    } else {
                        return new wipValidationResult(true);
                    }
                }
                else {
                    return new wipValidationResult(false);
                }

            }
            return new wipValidationResult(true);
        }

        return new wipValidationResult(false);
    }

    /**
     *
     * performReplaceContentValidation method will called upon doing the
     * ReplaceContent action validation. This validator logic allows or enables
     * the action on the workables for a particular user which satisifes the
     * following conditions. - The user had MODIFY_CONTENT permission set for
     * this workable. - The user and workable passes the Checkout validation.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param workable
     *            Object on which the action is going to operate.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     * @param wtuser
     *            temporary parameter until the deprecated public peer of this
     *            method is removed
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performReplaceContentValidation(
            UIValidationKey validation_key, Workable workable,
            UIValidationCriteria validation_criteria, String validation_type,
            WTUser user) throws WTException {
        if (user == null)
            user = (WTUser) validation_criteria.getUser().getPrincipal();

        if (wt.content.FormatContentHolder.class.isAssignableFrom(workable
                .getClass())) {
            Boolean prefRC = (Boolean) getPreferenceService().getValue(
                    validation_criteria.getParentContainer(),
                    "REPLACE_CONTENT_OFFERED",
                    PreferenceClient.WINDCHILL_CLIENT_NAME,
                    user);
            if (prefRC.booleanValue()) {
                /***************************************************************
                 * MODIFY_CONTENT access permissions be checked for replace
                 * content instead of MODIFY permission hence cannot use the
                 * Edit valiation.
                 */
                boolean modifycontentaccess = hasAccess(user, workable,
                        accessPermissions.MODIFY_CONTENT);
                if (modifycontentaccess) {
                    return performCheckoutValidation(validation_key, workable,
                            validation_criteria, validation_type, user);
                }
                else {
                    return new wipValidationResult(false, true);
                }
            } else {
                return new wipValidationResult(false, true);
            }
        }
        return new wipValidationResult(false);
    }

    /**
     *
     * performCheckoutAndDownloadValidation method will called upon doing the
     * Checkout And Download action validation. This validator logic allows or
     * enables the action on the workables for a particular user which satisifes
     * the following conditions.
     *  - The user has DOWNLOAD permission set for this workable. - The user and
     * workable passes the Checkout validation. - The workable has a primary
     * content to download.
     *
     * @param validation_key
     *            The String identifying the action or component being
     *            validated.
     * @param workable
     *            Object on which the action is going to operate.
     * @param validation_criteria
     *            Object holding information required to perform validation
     *            tasks.
     * @param validation_type
     *            Validation type. This is Full or Selected.
     * @param user
     *            temporary parameter until the deprecated public peer of this
     *            method is removed
     *
     * @return UIValidationResult
     */
    protected wipValidationResult performCheckoutAndDownloadValidation(
            UIValidationKey validation_key, Workable workable,
            UIValidationCriteria validation_criteria, String validation_type,
            WTUser user) throws WTException {

        ResourceBundle wipValidationRb = ResourceBundle.getBundle(
                WIP_VALIDATION_RESOURCE, WTContext.getContext().getLocale());
        if (user == null)
            user = (WTUser) validation_criteria.getUser().getPrincipal();
        if (wt.content.FormatContentHolder.class.isAssignableFrom(workable
                .getClass())) {
            // Perform limited validation
            if (validation_type.equals(LIMITED)) {
                boolean checkedOut = isCheckedOut(workable);
                logger.debug("checkedOut:");
                if (checkedOut)
                    return new wipValidationResult(false);
                else
                    return new wipValidationResult(true);
            } // Perform full validation
            else if (validation_type.equals(FULL)
                    || validation_type.equals(SELECTED)
                    || validation_type.equals(FORMSUBMISSION)) {

                // Check for the Download permission
                // for the user in validation criteria.
                boolean downloadaccess = hasAccess(user, workable,
                        accessPermissions.DOWNLOAD);
                logger.debug("downloadaccess:" + downloadaccess);
                if (downloadaccess == false) {
                    String feedback_message = wipValidationRb
                            .getString(WIPResource.CHECKOUT_WITHOUT_ACCESS);
                    UIValidationFeedbackMsg message = new UIValidationFeedbackMsg(
                            feedback_message, (FeedbackType.ERROR));
                    wipValidationResult result = new wipValidationResult(false, true);
                    result.addMessage(message);
                    return result;
                }

                /* Disable the action if there is no primary content. */
                boolean objhasprimarycontent = hasPrimaryContent(workable);
                logger.debug("objhasprimarycontent:" + objhasprimarycontent);
                if (!objhasprimarycontent) {
                    return new wipValidationResult(false);
                }

                return performCheckoutValidation(validation_key, workable,
                        validation_criteria, validation_type, user);

            }
        }
        return new wipValidationResult(false);
    }

    private WTReference getWTReference(Workable workable) {
        if (refFactory == null) {
            refFactory = new ReferenceFactory();
        }
        if (workable == null)
            return null;

        try {
            return refFactory.getReference(workable);
        } catch (WTException wte) {
            return null;
        }
    }

    /**
     * ************* below are methods for backward compatible and should be
     * removed later, 3/7/2007 ******************************
     */

    /** @deprecated, for backward compatible only */
    public UIValidationResult performCheckoutAndDownloadValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type) throws WTException {
        WTUser user = (WTUser) SessionHelper.manager.getPrincipal();
        return performCheckoutAndDownloadValidation(validation_key, workable,
                null, validation_type, user).getValidationResult(
                validation_key, getWTReference(workable), validation_type);
    }

    /** @deprecated, for backward compatible only */
    public UIValidationResult performReplaceContentValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type) throws WTException {
        WTUser user = (WTUser) SessionHelper.manager.getPrincipal();
        return performReplaceContentValidation(validation_key, workable, null,
                validation_type, user).getValidationResult(validation_key,
                getWTReference(workable), validation_type);
    }

    public UIValidationResult performCheckinValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type, WTUser user) throws WTException {
        return performCheckinValidation(validation_key, workable, null,
                validation_type, user).getValidationResult(validation_key,
                getWTReference(workable), validation_type);
    }

    public UIValidationResult performCheckoutValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type) throws WTException {
        WTUser user = (WTUser) SessionHelper.manager.getPrincipal();
        return performCheckoutValidation(validation_key, workable, null,
                validation_type, user).getValidationResult(validation_key,
                getWTReference(workable), validation_type);
    }

    public UIValidationResult performUndoCheckOutValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type) throws WTException {
        WTUser user = (WTUser) SessionHelper.manager.getPrincipal();
        return performUndoCheckoutValidation(validation_key, workable, null,
                validation_type, user).getValidationResult(validation_key,
                getWTReference(workable), validation_type);
    }

    /** @deprecated, for backward compatible only */
    public UIValidationResult performEditValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type, UIValidationCriteria validation_criteria)
            throws WTException {
        WTUser user = (WTUser) validation_criteria.getUser().getPrincipal();
        // (boolean result, UIValidationKey validation_key, WTReference wt_ref,
        // String validation_type)
        WTReference wt_ref = validation_criteria.getContextObject();
        return performEditValidation(workable, user).getValidationResult(
                validation_key, wt_ref, validation_type);
    }

    /** @deprecated, for backward compatible only */
    public UIValidationResult performCheckoutAndEditValidation(
            UIValidationKey validation_key, Workable workable,
            String validation_type, UIValidationCriteria validation_criteria)
            throws WTException {
        WTUser user = (WTUser) validation_criteria.getUser().getPrincipal();
        return performCheckoutAndEditValidation(validation_key, workable,
                validation_criteria, validation_type, user)
                .getValidationResult(validation_key, getWTReference(workable),
                        validation_type);
    }

    /*
     * Helper methods used to call methods on other classes. These methods were
     * pulled out so this class could be unit tested. THIS IS NOT THE PROPER WAY
     * TO DO UNIT TESTS. This is a hack because Not all of the following classes
     * implement interfaces.
     */

    protected boolean hasAccess(WTPrincipal userP, Workable workable,
            accessPermissions ap) throws WTException {
        return AccessControlHelper.manager.hasAccess(userP, workable, ap
                .getAccessPermission());
    }

    /**
     * Determines if this object is checked out by anyone. (pulled out into it's
     * own method for unit testing purposes) THIS IS NOT THE PROPER WAY TO DO
     * UNIT TESTS. This is a hack because Not all of the following classes
     * implement interfaces.
     *
     * @param workable
     * @return
     * @throws WTException
     */
    protected boolean isCheckedOut(Workable workable) throws WTException {
        // WorkInProgressHelper.
        return WorkInProgressHelper.isCheckedOut(workable);
    }

    /**
     * Determines if the current user was the one who checked out this document
     * (pulled out into it's own method for unit testing purposes) THIS IS NOT
     * THE PROPER WAY TO DO UNIT TESTS. This is a hack because Not all of the
     * following classes implement interfaces.
     *
     * @param workable
     * @param userP
     * @return
     * @throws WTException
     */
    protected boolean isCheckedOutToUser(Workable workable, WTPrincipal userP)
            throws WTException {
        return WorkInProgressHelper.isCheckedOut(workable, userP);
    }

    /**
     * Determines if the object is checked out to the sandbox (checked out to a
     * project from a product or library) (pulled out into it's own method for
     * unit testing purposes) THIS IS NOT THE PROPER WAY TO DO UNIT TESTS. This
     * is a hack because Not all of the following classes implement interfaces.
     *
     * @param workable
     * @return
     * @throws WTException
     */
    protected boolean isCheckedOutToSandbox(Workable workable)
            throws WTException {
        return SandboxHelper.isCheckedOutToSandbox(workable);
    }

    protected wt.inf.container.WTContainerService wTContainerService;

    private wt.inf.container.WTContainerService getWTContainerService() {
        if (wTContainerService == null) {
            wTContainerService = WTContainerHelper.service;
        }
        return wTContainerService;

    }

    /**
     * Determines if the object is the latest iteration of this version (pulled
     * out into it's own method for unit testing purposes) THIS IS NOT THE
     * PROPER WAY TO DO UNIT TESTS. This is a hack because Not all of the
     * following classes implement interfaces.
     *
     * @param iterated
     * @return
     */
    protected boolean isLatestIteration(Iterated iterated) {
        return VersionControlHelper.isLatestIteration(iterated);
    }

    protected boolean isCheckoutAllowed(Workable workable) throws WTException {
        return WorkInProgressHelper.service.isCheckoutAllowed(workable);
    }

    protected wt.preference.PreferenceService2 preferenceService;

    private wt.preference.PreferenceService2 getPreferenceService() {
        if (preferenceService == null) {
            preferenceService = PreferenceHelper.service;
        }
        return preferenceService;
    }

    /**
     * Determines if the workable has file primary content (pulled out into it's
     * own method for unit testing purposes) THIS IS NOT THE PROPER WAY TO DO
     * UNIT TESTS. This is a hack because Not all of the following classes
     * implement interfaces.
     *
     * @param workable
     * @return
     * @throws WTException
     */
    protected boolean hasPrimaryContent(Workable workable) throws WTException {
        if (workable instanceof FormatContentHolder) {
            FormatContentHolder holder = null;
            try {
                holder = (FormatContentHolder) ContentHelper.service
                        .getContents((FormatContentHolder) workable);
            } catch (PropertyVetoException pve) {
                System.err.println(pve);
            }
            ContentItem item = ContentHelper.getPrimary(holder);
            if (item == null || item instanceof URLData
                    || item instanceof ExternalStoredData) {
                return false;
            }
            return true;
        }
        return false;
    }

} // end class