com.aurel.track.admin.project.ProjectConfigBL.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.admin.project.ProjectConfigBL.java

Source

/**
 * Genji Scrum Tool and Issue Tracker
 * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions
    
 * <a href="http://www.trackplus.com">Genji Scrum Tool</a>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id:$ */

package com.aurel.track.admin.project;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import com.aurel.track.admin.projectCopy.ProjectCopyBL;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import com.aurel.track.accessControl.AccessBeans;
import com.aurel.track.admin.customize.category.CategoryBL;
import com.aurel.track.admin.customize.category.filter.tree.design.FilterUpperTO;
import com.aurel.track.admin.customize.category.report.ReportBL;
import com.aurel.track.admin.customize.lists.ListBL;
import com.aurel.track.admin.customize.lists.customList.CustomListBL;
import com.aurel.track.admin.customize.lists.systemOption.IssueTypeBL;
import com.aurel.track.admin.customize.lists.systemOption.PriorityBL;
import com.aurel.track.admin.customize.lists.systemOption.SeverityBL;
import com.aurel.track.admin.customize.lists.systemOption.StatusBL;
import com.aurel.track.admin.customize.objectStatus.SystemStatusBL;
import com.aurel.track.admin.customize.projectType.ProjectTypesBL;
import com.aurel.track.admin.customize.projectType.assignments.itemType.ProjectTypeItemTypePriorityAssignmentFacade;
import com.aurel.track.admin.customize.projectType.assignments.itemType.ProjectTypeItemTypeSeverityAssignmentFacade;
import com.aurel.track.admin.customize.projectType.assignments.itemType.ProjectTypeItemTypeStatusAssignmentFacade;
import com.aurel.track.admin.customize.projectType.assignments.simple.ProjectTypeChildrenAssignmentFacade;
import com.aurel.track.admin.customize.projectType.assignments.simple.ProjectTypeItemTypeAssignmentFacade;
import com.aurel.track.admin.customize.role.RoleBL;
import com.aurel.track.admin.customize.treeConfig.TreeConfigBL;
import com.aurel.track.admin.customize.treeConfig.field.FieldBL;
import com.aurel.track.admin.project.assignments.RoleAssignmentsBL;
import com.aurel.track.admin.project.release.ProjectReleaseTokens;
import com.aurel.track.admin.project.release.ReleaseBL;
import com.aurel.track.admin.project.release.ReleaseConfigBL;
import com.aurel.track.admin.project.release.ReleaseJSON;
import com.aurel.track.admin.server.siteConfig.SiteConfigBL;
import com.aurel.track.admin.user.person.PersonBL;
import com.aurel.track.admin.user.userLevel.UserLevelBL.USER_LEVEL_ACTION_IDS;
import com.aurel.track.beans.ILabelBean;
import com.aurel.track.beans.TExportTemplateBean;
import com.aurel.track.beans.TFieldConfigBean;
import com.aurel.track.beans.TInitStateBean;
import com.aurel.track.beans.TListBean;
import com.aurel.track.beans.TListTypeBean;
import com.aurel.track.beans.TPersonBean;
import com.aurel.track.beans.TPriorityBean;
import com.aurel.track.beans.TProjectBean;
import com.aurel.track.beans.TProjectTypeBean;
import com.aurel.track.beans.TReleaseBean;
import com.aurel.track.beans.TRoleBean;
import com.aurel.track.beans.TSeverityBean;
import com.aurel.track.beans.TSiteBean;
import com.aurel.track.beans.TStateBean;
import com.aurel.track.beans.TSystemStateBean;
import com.aurel.track.beans.TWorkItemBean;
import com.aurel.track.cluster.ClusterMarkChangesBL;
import com.aurel.track.dao.DAOFactory;
import com.aurel.track.dao.InitStatusDAO;
import com.aurel.track.dao.ProjectDAO;
import com.aurel.track.dao.WorkItemDAO;
import com.aurel.track.fieldType.constants.BooleanFields;
import com.aurel.track.fieldType.constants.SystemFields;
import com.aurel.track.fieldType.runtime.base.LookupContainer;
import com.aurel.track.fieldType.runtime.bl.AttributeValueBL;
import com.aurel.track.fieldType.runtime.bl.FieldRuntimeBL;
import com.aurel.track.item.ItemBL;
import com.aurel.track.item.budgetCost.AccountingBL;
import com.aurel.track.item.history.FieldChangeBL;
import com.aurel.track.itemNavigator.ItemNavigatorBL.QUERY_TYPE;
import com.aurel.track.itemNavigator.lastExecuted.LastExecutedBL;
import com.aurel.track.json.ControlError;
import com.aurel.track.json.JSONUtility;
import com.aurel.track.lucene.LuceneUtil;
import com.aurel.track.lucene.index.LuceneIndexer;
import com.aurel.track.lucene.index.associatedFields.IAssociatedFieldsIndexer;
import com.aurel.track.lucene.index.listFields.NotLocalizedListIndexer;
import com.aurel.track.prop.ApplicationBean;
import com.aurel.track.resources.LocalizeUtil;
import com.aurel.track.util.EqualUtils;
import com.aurel.track.util.GeneralUtils;
import com.aurel.track.util.PropertiesHelper;

/**
 * Business logic for configuring a project
 * @author Tamas Ruff
 *
 */
public class ProjectConfigBL {

    public interface PROJECT_ASSIGNMENTS {
        public final static int GENERAL = 1;
        public final static int ASSIGN_ROLES = 2;
        public final static int PHASES = 3;
        //public final static int RELEASES = 4; //only releases?
        //public final static int RELEASES_AND_PHASES = 4;
        public final static int LISTS = 4;
        public final static int FILTERS = 5;
        public final static int REPORT_TEMPLTATES = 6;
        public final static int SCREEN_ASSIGNMENT = 7;
        public final static int FIELD_CONFIGURATION = 8;
        public final static int ASSIGN_ACCOUNTS = 9;
        public final static int AUTOMAIL = 10;
        public final static int COCKPIT = 11;
        public final static int VERSION_CONTROL = 12;
        public final static int ASSIGN_WORKFLOW = 13;
        public final static int EXPORT_TO_MSPROJECT = 14;
        public final static int IMPORT_FROM_MSPROJECT = 15;
    }

    public interface EMAIL_PROTOCOL {
        public final static String POP3 = "pop3";
        public final static String IMAP = "imap";
    }

    public interface EMAIL_DEFAULT_PORTS {
        public final static int POP3 = 110;
        public final static int POP3_SSL = 995;
        public final static int IMAP = 143;
        public final static int IMAP_SSL = 993;
    }

    private static final Logger LOGGER = LogManager.getLogger(ProjectConfigBL.class);
    private static ProjectDAO projectDAO = DAOFactory.getFactory().getProjectDAO();
    private static WorkItemDAO workItemDAO = DAOFactory.getFactory().getWorkItemDAO();
    private static InitStatusDAO initStatusDAO = DAOFactory.getFactory().getInitStatusDAO();

    private static String ASSIGN_ROLES_CLASS = "roles-ticon";
    private static String RELEASE_ICON_CLASS = ReleaseBL.RELEASE_ICON_CLASS;
    private static String LIST_ICON_CLASS = "picklists-ticon";
    private static String FILTER_ICON_CLASS = "filter-ticon";
    private static String REPORT_ICON_CLASS = "report-ticon";
    private static String ACCOUNT_ICON_CLASS = "account-ticon";
    private static String FIELD_CONFIG_ICON_CLASS = "fields-ticon";
    private static String SCREEN_ASSIGN_ICON_CLASS = "forms-ticon";
    private static String AUTOMAIL_ASSIGNMENT_ICON = "automailc-ticon";
    private static String COCKPIT_ICON_CLASS = "cockpit-ticon";
    private static String VERSION_CONTROL_ICON_CLASS = "versionControl-ticon";
    private static String WORKFLOW_ICON_CLASS = "workflow-ticon";
    private static String MS_PROJECT_CLASS = "msProject-ticon";

    static String loadConfig(Integer projectID, Integer personID, Map<String, Object> session, Locale locale) {
        boolean hasPrivateWorkspace = ProjectConfigBL.hasPrivateProject(personID);
        boolean templateIsActive = false;
        if (projectID != null) {
            Integer statusFlag = SystemStatusBL.getStatusFlag(projectID, TSystemStateBean.ENTITYFLAGS.PROJECTSTATE);
            if (statusFlag != null) {
                if (statusFlag.intValue() == TSystemStateBean.STATEFLAGS.ACTIVE) {
                    templateIsActive = true;
                }
                if (statusFlag.intValue() == TSystemStateBean.STATEFLAGS.CLOSED) {
                    templateIsActive = false;
                }
            }
        }
        String lastSelectedSection = (String) session.get(ProjectAction.PROJECT_ADMIN_LAST_SELECTED_SECTION);
        Integer lastSelectedTab = (Integer) session.get(ProjectAction.PROJECT_ADMIN_LAST_SELECTED_TAB);

        String localizedMain = LocalizeUtil
                .getLocalizedTextFromApplicationResources(ReleaseConfigBL.RELEAESE_KEY_IN_OP, locale);
        String localizedChild = null;
        boolean showClosedReleases = true;
        if (projectID != null) {
            TProjectBean projectBean = ProjectBL.loadByPrimaryKey(projectID);
            if (projectBean != null) {
                showClosedReleases = PropertiesHelper.getBooleanProperty(projectBean.getMoreProps(),
                        TProjectBean.MOREPPROPS.SHOW_ARCHIVED_RELEASE);
                Integer projectTypeID = projectBean.getProjectType();
                if (projectTypeID != null) {
                    TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
                    if (projectTypeBean != null) {
                        Integer projectTypeFlag = projectTypeBean.getProjectTypeFlag();
                        localizedChild = ReleaseConfigBL.getLocalizedChildLabel(projectTypeFlag, locale);
                    }
                }
            }
        }
        if (localizedChild == null) {
            localizedChild = LocalizeUtil
                    .getLocalizedTextFromApplicationResources("admin.project.release.childPhase.general", locale);
        }
        //TODO: if only one of releaseNoticed and releaseseScheduled is used take the localizedMain from Field config: uncomment this
        /*TFieldConfigBean fieldConfigBean = LocalizeUtil.localizeFieldConfig(
              FieldRuntimeBL.getValidConfig(SystemFields.INTEGER_RELEASESCHEDULED, null, projectID), locale);
        if (fieldConfigBean!=null) {
           localizedMain = fieldConfigBean.getLabel();
        }*/

        /*if (localizedMain==null) {
           localizedMain = LocalizeUtil.localizeRelease(locale);
        }*/

        return ProjectJSON.encodeProjectLastSelections(hasPrivateWorkspace, templateIsActive, lastSelectedSection,
                lastSelectedTab, localizedMain, localizedChild, showClosedReleases);

    }

    /**
     * Gets the hard coded assignment nodes for a selected project
     * @param projectID
     * @param locale
     * @return
     */
    static String getChildren(Integer projectID, TPersonBean personBean, Locale locale) {
        if (projectID == null) {
            return "[]";
        }
        TProjectTypeBean projectTypeBean = null;
        TProjectBean projectBean = ProjectBL.loadByPrimaryKey(projectID);
        if (projectBean == null) {
            LOGGER.error("No project found for id:" + projectID);
            return "[]";
        }
        boolean isPrivateProject = false;
        Integer projectTypeID = projectBean.getProjectType();
        if (projectTypeID != null) {
            projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
        }
        isPrivateProject = BooleanFields.fromStringToBoolean(projectTypeBean.getDefaultForPrivate());
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");

        boolean showRoles = !isPrivateProject;
        boolean showRelease = !isPrivateProject && mightHaveRelease(projectTypeBean, projectBean);
        Map<Integer, Boolean> userLevelMap = personBean.getUserLevelMap();
        boolean sys = personBean.isSys();
        //project specific lists
        Boolean manageLists = null;
        if (userLevelMap != null) {
            manageLists = userLevelMap.get(USER_LEVEL_ACTION_IDS.LISTS);
        }
        boolean showManageList = sys || (manageLists != null && manageLists.booleanValue());
        Boolean manageFilters = null;
        if (userLevelMap != null) {
            manageFilters = userLevelMap.get(USER_LEVEL_ACTION_IDS.MANAGE_FILTERS);
        }
        boolean showManageFilters = sys || (manageFilters != null && manageFilters.booleanValue());
        Boolean manageReports = null;
        if (userLevelMap != null) {
            manageReports = userLevelMap.get(USER_LEVEL_ACTION_IDS.REPORT_TEMPLATES);
        }
        boolean showManageReports = sys || (manageReports != null && manageReports.booleanValue());
        Boolean manageForms = null;
        if (userLevelMap != null) {
            manageForms = userLevelMap.get(USER_LEVEL_ACTION_IDS.FORMS);
        }
        boolean showManageForms = sys || (manageForms != null && manageForms.booleanValue() && !isPrivateProject);
        Boolean manageFields = null;
        if (userLevelMap != null) {
            manageFields = userLevelMap.get(USER_LEVEL_ACTION_IDS.FIELDS);
        }
        boolean showManageFields = sys
                || (manageFields != null && manageFields.booleanValue() && !isPrivateProject);
        Boolean canSeeWorkflows = null;
        if (userLevelMap != null) {
            canSeeWorkflows = userLevelMap.get(USER_LEVEL_ACTION_IDS.WORKFLOWS);
        }
        boolean showWorkflow = !isPrivateProject
                && (sys || (canSeeWorkflows != null && canSeeWorkflows.booleanValue()))
                && !ApplicationBean.getInstance().isGenji();
        boolean showAssignAccount = !ApplicationBean.getInstance().isGenji() && projectTypeBean != null
                && mightHaveAccounting(projectTypeBean, projectBean);
        boolean showAutomail = !isPrivateProject;
        boolean showCockpit = !ApplicationBean.getInstance().isGenji();
        boolean showVersionControl = projectTypeBean != null && projectTypeBean.getUseVersionControl()
                && !isPrivateProject;

        boolean hasNext = showRoles || showRelease || showManageList || showManageFilters || showManageReports
                || showManageForms || showManageFields || showWorkflow || showAssignAccount || showAutomail
                || showCockpit || showVersionControl;
        //main project settings
        ProjectTreeNodeTO mainProjectDetails = new ProjectTreeNodeTO(
                String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.GENERAL),
                LocalizeUtil.getLocalizedTextFromApplicationResources("admin.project.lbl.general", locale),
                ProjectBL.PROJECT_ICON_CLASS);
        mainProjectDetails.setPrivateProject(isPrivateProject);
        stringBuilder.append(ProjectJSON.getChildJSON(mainProjectDetails, !hasNext));

        //assigned roles
        if (showRoles) {
            hasNext = showRelease || showManageList || showManageFilters || showManageReports || showManageForms
                    || showManageFields || showWorkflow || showAssignAccount || showAutomail || showCockpit
                    || showVersionControl;
            String assignRolesLabel = LocalizeUtil
                    .getLocalizedTextFromApplicationResources("admin.customize.role.lbl.roles", locale);
            assignRolesLabel = LocalizeUtil.getParametrizedString("common.lbl.assign",
                    new Object[] { assignRolesLabel }, locale);
            String roleAssignmentBranchNode = RoleAssignmentsBL.getProjectBranchNodeID(projectID);
            ProjectTreeNodeTO assignRoles = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.ASSIGN_ROLES), assignRolesLabel,
                    ASSIGN_ROLES_CLASS, roleAssignmentBranchNode);
            stringBuilder.append(ProjectJSON.getChildJSON(assignRoles, !hasNext));
        }
        //releases
        if (showRelease) {
            hasNext = showManageList || showManageFilters || showManageReports || showManageForms
                    || showManageFields || showWorkflow || showAssignAccount || showAutomail || showCockpit
                    || showVersionControl;
            String reasesBranchRoot = ReleaseConfigBL.getProjectBranchNodeID(projectID);
            ProjectTreeNodeTO phases = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.PHASES),
                    LocalizeUtil.getLocalizedTextFromApplicationResources(ReleaseConfigBL.RELEAESE_KEY, locale),
                    RELEASE_ICON_CLASS, reasesBranchRoot);
            stringBuilder.append(ProjectJSON.getChildJSON(phases, !hasNext));
        }
        if (showManageList) {
            hasNext = showManageFilters || showManageReports || showManageForms || showManageFields || showWorkflow
                    || showAssignAccount || showAutomail || showCockpit || showVersionControl;
            String projectListsBranchRoot = ListBL.getProjectBranchNodeID(projectID);
            ProjectTreeNodeTO projectSpecificLists = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.LISTS),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.admin.custom.list", locale),
                    LIST_ICON_CLASS, projectListsBranchRoot);
            stringBuilder.append(ProjectJSON.getChildJSON(projectSpecificLists, !hasNext));
        }
        //project specific issue filters
        if (showManageFilters) {
            hasNext = showManageReports || showManageForms || showManageFields || showWorkflow || showAssignAccount
                    || showAutomail || showCockpit || showVersionControl;
            String projectFilterBranchRoot = CategoryBL.getProjectBranchNodeID(projectID,
                    CategoryBL.CATEGORY_TYPE.ISSUE_FILTER_CATEGORY);
            ProjectTreeNodeTO projectSpecificFilters = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.FILTERS),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.admin.custom.queryFilters", locale),
                    FILTER_ICON_CLASS, projectFilterBranchRoot);
            stringBuilder.append(ProjectJSON.getChildJSON(projectSpecificFilters, !hasNext));
        }
        //project specific reports
        if (showManageReports) {
            hasNext = showManageForms || showManageFields || showWorkflow || showAssignAccount || showAutomail
                    || showCockpit || showVersionControl;
            String projectReportBranchRoot = CategoryBL.getProjectBranchNodeID(projectID,
                    CategoryBL.CATEGORY_TYPE.REPORT_CATEGORY);
            ProjectTreeNodeTO projectSpecificReports = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.REPORT_TEMPLTATES), LocalizeUtil
                            .getLocalizedTextFromApplicationResources("menu.admin.custom.reportTemplates", locale),
                    REPORT_ICON_CLASS, projectReportBranchRoot);
            stringBuilder.append(ProjectJSON.getChildJSON(projectSpecificReports, !hasNext));
        }
        //screen assignment
        if (showManageForms) {
            hasNext = showManageFields || showWorkflow || showAssignAccount || showAutomail || showCockpit
                    || showVersionControl;
            String screenAssignmentBranchRoot = TreeConfigBL.getProjectBranchNodeID(projectID,
                    TreeConfigBL.SCREEN_CONFIG);
            ProjectTreeNodeTO projectSpecificScreenAssignments = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.SCREEN_ASSIGNMENT),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.admin.custom.customForms", locale),
                    SCREEN_ASSIGN_ICON_CLASS, screenAssignmentBranchRoot);
            stringBuilder.append(ProjectJSON.getChildJSON(projectSpecificScreenAssignments, !hasNext));
        }
        //field config
        if (showManageFields) {
            hasNext = showWorkflow || showAssignAccount || showAutomail || showCockpit || showVersionControl;
            String fieldConfigBranchRoot = TreeConfigBL.getProjectBranchNodeID(projectID,
                    TreeConfigBL.FIELD_CONFIG);
            ProjectTreeNodeTO projectSpecificFieldConfigs = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.FIELD_CONFIGURATION),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.admin.custom.customField", locale),
                    FIELD_CONFIG_ICON_CLASS, fieldConfigBranchRoot);
            stringBuilder.append(ProjectJSON.getChildJSON(projectSpecificFieldConfigs, !hasNext));
        }
        //workflow assignment
        if (showWorkflow) {
            hasNext = showAssignAccount || showAutomail || showCockpit || showVersionControl;
            String workflowAssignmentBranchRoot = TreeConfigBL.getProjectBranchNodeID(/*projectID*/null,
                    TreeConfigBL.WORKFLOW_CONFIG);
            ProjectTreeNodeTO projectSpecificWorkflowAssignments = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.ASSIGN_WORKFLOW),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.admin.custom.workflow", locale),
                    WORKFLOW_ICON_CLASS, workflowAssignmentBranchRoot);
            projectSpecificWorkflowAssignments.setProjectID(projectID);
            stringBuilder.append(ProjectJSON.getChildJSON(projectSpecificWorkflowAssignments, !hasNext));
        }
        //assigned accounts
        if (showAssignAccount) {
            hasNext = showAutomail || showCockpit || showVersionControl;
            String assignAccountLabel = LocalizeUtil
                    .getLocalizedTextFromApplicationResources("admin.customize.account.lbl.accounts", locale);
            assignAccountLabel = LocalizeUtil.getParametrizedString("common.lbl.assign",
                    new Object[] { assignAccountLabel }, locale);
            ProjectTreeNodeTO assignAccounts = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.ASSIGN_ACCOUNTS), assignAccountLabel,
                    ACCOUNT_ICON_CLASS);
            stringBuilder.append(ProjectJSON.getChildJSON(assignAccounts, !hasNext));
        }
        //project specific automail
        if (showAutomail) {
            hasNext = showCockpit || showVersionControl;
            ProjectTreeNodeTO automail = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.AUTOMAIL),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.admin.custom.automail", locale),
                    AUTOMAIL_ASSIGNMENT_ICON);
            stringBuilder.append(ProjectJSON.getChildJSON(automail, !hasNext));
        }
        //project cockpit

        if (showCockpit) {
            hasNext = showVersionControl;
            ProjectTreeNodeTO cockpit = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.COCKPIT),
                    LocalizeUtil.getLocalizedTextFromApplicationResources("menu.cockpit", locale),
                    COCKPIT_ICON_CLASS);
            stringBuilder.append(ProjectJSON.getChildJSON(cockpit, !hasNext));
        }
        //version control
        if (showVersionControl) {
            ProjectTreeNodeTO versionControl = new ProjectTreeNodeTO(
                    String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.VERSION_CONTROL),
                    LocalizeUtil.getLocalizedTextFromApplicationResources(
                            "admin.customize.projectType.lbl.versionControl", locale),
                    VERSION_CONTROL_ICON_CLASS);
            stringBuilder.append(ProjectJSON.getChildJSON(versionControl, true));
        }
        //export to MS Project
        /*if (useExportMsProject) {
           hasNext=useImportMsProject;
           ProjectTreeNodeTO exportToMsProject = new ProjectTreeNodeTO(
           String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.EXPORT_TO_MSPROJECT),
           LocalizeUtil.getLocalizedTextFromApplicationResources("admin.project.export.lbl.msProject", locale),
           MS_PROJECT_CLASS);
           stringBuilder.append(ProjectJSON.getChildJSON(exportToMsProject,!hasNext));
        }*/
        //import from MS Project
        /*if (useImportMsProject) {
           ProjectTreeNodeTO importFromMsProject = new ProjectTreeNodeTO(
           String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.IMPORT_FROM_MSPROJECT),
           LocalizeUtil.getLocalizedTextFromApplicationResources("admin.project.import.lbl.msProject", locale),
           MS_PROJECT_CLASS);
           stringBuilder.append(ProjectJSON.getChildJSON(importFromMsProject, true));
        }*/
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    static boolean hasPrivateProject(Integer personID) {
        return ProjectBL.loadPrivateProject(personID);
    }

    /**
     * Loads the details for a new/existing project 
     * @param projectID
     * @param add
     * @param locale
     * @return
     */
    static String getProjectDetail(Integer projectID, boolean add, boolean addAsSubproject,
            boolean addAsPrivateProject, TPersonBean personBean, Locale locale) {
        boolean isSubproject = false;
        ProjectBaseTO projectBaseTO = new ProjectBaseTO();
        ProjectAccountingTO projectAccountingTO = null;
        ProjectDefaultsTO projectDefaultsTO = new ProjectDefaultsTO();

        TProjectBean projectBean = null;
        Integer projectTypeID = null;
        List<ILabelBean> projectTypeBeans = null;
        if (add) {
            //add project
            Integer parentProjectID = null;
            if (addAsSubproject) {
                //get parent project to inherit from
                projectBean = ProjectBL.loadByPrimaryKey(projectID);
                parentProjectID = projectBean.getProjectType();
            }
            if (projectBean == null) {
                projectBean = new TProjectBean();
            }
            if (addAsPrivateProject) {
                //get private projectTypes
                projectTypeBeans = (List) ProjectTypesBL.loadPrivate();
            } else {
                //get non private projectTypes
                projectTypeBeans = ProjectTypeChildrenAssignmentFacade.getInstance()
                        .getPossibleBeans(parentProjectID, null, locale);
            }
        } else {
            projectBean = ProjectBL.loadByPrimaryKey(projectID);
            if (projectBean != null) {
                isSubproject = projectBean.getParent() != null;
                projectTypeID = projectBean.getProjectType();
                if (projectTypeID != null && projectTypeID < 0) {
                    //get private project types
                    projectTypeBeans = (List) ProjectTypesBL.loadPrivate();
                } else {
                    //get non private project types
                    Integer parentProjectTypeID = null;
                    if (isSubproject) {
                        Integer parentProjectID = projectBean.getParent();
                        TProjectBean parentProjectBean = LookupContainer.getProjectBean(parentProjectID);
                        if (parentProjectBean != null) {
                            parentProjectTypeID = parentProjectBean.getProjectType();
                        }
                    }
                    projectTypeBeans = ProjectTypeChildrenAssignmentFacade.getInstance()
                            .getPossibleBeans(parentProjectTypeID, projectTypeID, locale);
                }
            }
        }

        if (projectTypeID == null) {
            if (projectTypeBeans != null && !projectTypeBeans.isEmpty()) {
                projectTypeID = projectTypeBeans.get(0).getObjectID();
            }
        }
        projectBaseTO.setProjectTypeID(projectTypeID);
        projectBaseTO.setProjectTypeList(projectTypeBeans);
        List<TPersonBean> managerBeans = null;
        List<TPersonBean> responsibleBeans = null;
        if (addAsPrivateProject || projectTypeID < 0) {
            //for new or existing private project limit the managers and responsibles to the current person 
            managerBeans = new LinkedList<TPersonBean>();
            responsibleBeans = new LinkedList<TPersonBean>();
            managerBeans.add(personBean);
            responsibleBeans.add(personBean);
        } else {
            //initialize project defaults
            managerBeans = PersonBL.loadActivePersons();
            responsibleBeans = PersonBL.loadActivePersonsAndGroups();
        }
        projectDefaultsTO.setManagerList(managerBeans);
        projectDefaultsTO.setResponsibleList(responsibleBeans);
        Map<Integer, String> invalidDefaults = setSystemLists(projectBean, projectDefaultsTO, projectTypeID, locale,
                false);
        List<TSystemStateBean> projectStatusList = SystemStatusBL
                .getStatusOptions(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, locale);
        projectBaseTO.setProjectStatusList(projectStatusList);
        TSiteBean siteBean = SiteConfigBL.loadTSite();
        if (siteBean != null) {
            projectBaseTO.setHasPrefix(siteBean.getProjectSpecificIDsOn());
        }
        if (projectTypeID != null) {
            TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
            if (projectTypeBean != null) {
                projectBaseTO.setHasRelease(projectTypeBean.getUseReleases());
                boolean projectTypeHasAccountig = projectTypeBean.getUseAccounting();
                if (!ApplicationBean.getInstance().isGenji() && projectTypeHasAccountig) {
                    projectAccountingTO = ProjectBL.getProjectAccountingTO(projectID, add, addAsSubproject, false,
                            projectTypeBean, false);
                    invalidDefaults.putAll(
                            ProjectBL.setAccountList(projectID, projectAccountingTO, add, addAsSubproject, locale));
                    projectAccountingTO.setWorkUnitList(AccountingBL.getTimeUnitOptionsList(locale));
                }
            }
        }
        if (add) {
            //project base
            if (addAsPrivateProject) {
                projectBaseTO.setLabel(LocalizeUtil
                        .getLocalizedTextFromApplicationResources("admin.project.lbl.privateProjectName", locale));
            }
            Integer projectStatus = null;
            Integer defaultManager = null;
            Integer defaultResponsible = null;
            Integer prefillBy = null;
            if (addAsSubproject) {
                //inherit from parent project
                projectStatus = projectBean.getStatus();
                String moreProps = projectBean.getMoreProps();
                projectBaseTO.setLinking(projectBean.isLinkingActive());
                projectBaseTO.setUseRelease(
                        PropertiesHelper.getBooleanProperty(moreProps, TProjectBean.MOREPPROPS.USE_RELEASE));
                prefillBy = PropertiesHelper.getIntegerProperty(moreProps, TProjectBean.MOREPPROPS.PREFILL);
                defaultManager = projectBean.getDefaultManagerID();
                defaultResponsible = projectBean.getDefaultOwnerID();
            }
            if (projectStatus == null) {
                if (projectStatusList != null && !projectStatusList.isEmpty()) {
                    projectStatus = projectStatusList.get(0).getObjectID();
                }
            }
            projectBaseTO.setProjectStatusID(projectStatus);
            if (defaultManager == null) {
                if (managerBeans != null && !managerBeans.isEmpty()) {
                    defaultManager = managerBeans.get(0).getObjectID();
                }
            }
            projectDefaultsTO.setDefaultManagerID(defaultManager);
            if (defaultResponsible == null) {
                if (responsibleBeans != null && !responsibleBeans.isEmpty()) {
                    defaultResponsible = responsibleBeans.get(0).getObjectID();
                }
            }
            projectDefaultsTO.setDefaultResponsibleID(defaultResponsible);
            if (prefillBy == null) {
                prefillBy = Integer.valueOf(TProjectBean.PREFILL.LASTWORKITEM);
            }
            projectDefaultsTO.setPrefillBy(prefillBy);

        } else {
            //project base
            String moreProps = projectBean.getMoreProps();
            projectBaseTO.setLabel(projectBean.getLabel());
            projectBaseTO.setDescription(projectBean.getDescription());
            projectBaseTO.setProjectTypeID(projectBean.getProjectType());
            projectBaseTO.setProjectStatusID(projectBean.getStatus());
            projectBaseTO.setLinking(
                    PropertiesHelper.getBooleanProperty(moreProps, TProjectBean.MOREPPROPS.LINKING_PROPERTY));
            projectBaseTO.setPrefix(projectBean.getPrefix());
            projectBaseTO.setUseRelease(
                    PropertiesHelper.getBooleanProperty(moreProps, TProjectBean.MOREPPROPS.USE_RELEASE));
            //project defaults
            //default manager
            Integer defaultManager = projectBean.getDefaultManagerID();
            if (defaultManager == null) {
                if (managerBeans != null && !managerBeans.isEmpty()) {
                    defaultManager = managerBeans.get(0).getObjectID();
                }
            } else {
                boolean found = foundInList((List) managerBeans, defaultManager);
                if (!found) {
                    TPersonBean defaultManagerPerson = LookupContainer.getPersonBean(defaultManager);
                    if (defaultManagerPerson != null) {
                        //person was deactivated add forced to the list  
                        managerBeans.add(0, defaultManagerPerson);
                        Object[] arguments = new Object[] { defaultManagerPerson.getLabel(), FieldRuntimeBL
                                .getLocalizedDefaultFieldLabel(SystemFields.INTEGER_MANAGER, locale) };
                        invalidDefaults.put(SystemFields.INTEGER_MANAGER, LocalizeUtil
                                .getParametrizedString("admin.project.err.invalidPerson", arguments, locale));
                    }
                }
            }
            projectDefaultsTO.setDefaultManagerID(defaultManager);
            //default responsible
            Integer defaultResponsible = projectBean.getDefaultOwnerID();
            if (defaultResponsible == null) {
                if (responsibleBeans != null && !responsibleBeans.isEmpty()) {
                    defaultResponsible = responsibleBeans.get(0).getObjectID();
                }
            } else {
                boolean found = foundInList((List) responsibleBeans, defaultResponsible);
                if (!found) {
                    TPersonBean defaultResponsiblePerson = LookupContainer.getPersonBean(defaultResponsible);
                    if (defaultResponsiblePerson != null) {
                        //person was deactivated add forced to the list
                        responsibleBeans.add(0, defaultResponsiblePerson);
                        Object[] arguments = new Object[] { defaultResponsiblePerson.getLabel(), FieldRuntimeBL
                                .getLocalizedDefaultFieldLabel(SystemFields.INTEGER_RESPONSIBLE, locale) };
                        invalidDefaults.put(SystemFields.INTEGER_RESPONSIBLE, LocalizeUtil
                                .getParametrizedString("admin.project.err.invalidPerson", arguments, locale));
                    }
                }
            }
            projectDefaultsTO.setDefaultResponsibleID(defaultResponsible);
            Integer prefillBy = getPrefillValue(projectBean);
            if (prefillBy == null) {
                prefillBy = Integer.valueOf(TProjectBean.PREFILL.LASTWORKITEM);
            }
            projectDefaultsTO.setPrefillBy(prefillBy);

        }
        return ProjectJSON.loadProjectDetailJSON(isSubproject, projectBaseTO, projectAccountingTO,
                projectDefaultsTO, invalidDefaults);
    }

    /**
     * Refresh the project accounting/release/system lists after projectType change 
     * @param add
     * @param addAsSubproject
     * @param projectID
     * @param submittedAccountingInherited
     * @param projectDefaultsTO
     * @param projectTypeID
     * @param locale
     */
    static String getProjectTypeChangeDetail(boolean add, boolean addAsSubproject, Integer projectID,
            boolean submittedAccountingInherited, ProjectDefaultsTO projectDefaultsTO, Integer projectTypeID,
            Locale locale) {
        TProjectBean projectBean;
        if (add) {
            projectBean = new TProjectBean();
        } else {
            projectBean = ProjectBL.loadByPrimaryKey(projectID);
        }
        TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
        boolean projectTypeHasAccountig = false;
        boolean hasReleases = false;
        ProjectAccountingTO projectAccountingTO = null;
        Map<Integer, String> invalidDefaults = new HashMap<Integer, String>();
        if (projectTypeBean != null) {
            projectTypeHasAccountig = projectTypeBean.getUseAccounting();
            if (!ApplicationBean.getInstance().isGenji() && projectTypeHasAccountig) {
                projectAccountingTO = ProjectBL.getProjectAccountingTO(projectID, add, addAsSubproject, true,
                        projectTypeBean, submittedAccountingInherited);
                invalidDefaults.putAll(
                        ProjectBL.setAccountList(projectID, projectAccountingTO, add, addAsSubproject, locale));
                projectAccountingTO.setWorkUnitList(AccountingBL.getTimeUnitOptionsList(locale));
            }
            hasReleases = projectTypeBean.getUseReleases();
        }
        invalidDefaults.putAll(setSystemLists(projectBean, projectDefaultsTO, projectTypeID, locale, true));
        return ProjectJSON.loadProjectTypeChangeJSON(projectAccountingTO, hasReleases, projectDefaultsTO,
                invalidDefaults);

    }

    /**
     * Sets the list entries and list values for system lists
     * @param projectBean
     * @param projectDefaultsTO
     * @param projectTypeID
     * @param locale
     * @param afterProjectTypeChange whether this is called after a projectType change or after a project load
     * @return
     */
    static Map<Integer, String> setSystemLists(TProjectBean projectBean, ProjectDefaultsTO projectDefaultsTO,
            Integer projectTypeID, Locale locale, boolean afterProjectTypeChange) {
        Map<Integer, String> invalidFieldDefaults = new HashMap<Integer, String>();
        Integer projectID = projectBean.getObjectID();
        String moreProps = projectBean.getMoreProps();
        //issue type
        Integer issueTypeID = null;
        if (afterProjectTypeChange) {
            issueTypeID = projectDefaultsTO.getDefaultIssueTypeID();
        } else {
            issueTypeID = PropertiesHelper.getIntegerProperty(moreProps, TProjectBean.MOREPPROPS.DEFAULT_ISSUETYPE);
        }
        List<ILabelBean> allowedIssueTypeBeans = ProjectTypeItemTypeAssignmentFacade.getInstance()
                .getPossibleBeans(projectTypeID, issueTypeID, locale);
        projectDefaultsTO.setIssueTypeList(allowedIssueTypeBeans);
        if (allowedIssueTypeBeans != null && !allowedIssueTypeBeans.isEmpty()) {
            if (issueTypeID == null) {
                //either new or existing but the initial status was not yet set
                issueTypeID = allowedIssueTypeBeans.get(0).getObjectID();
            } else {
                //existing project, default issueType was set
                boolean issueTypeValid = foundInList(allowedIssueTypeBeans, issueTypeID);
                if (!issueTypeValid) {
                    if (afterProjectTypeChange) {
                        //not found after projectType change, set the first valid one
                        issueTypeID = allowedIssueTypeBeans.get(0).getObjectID();
                    } else {
                        //by project loading leave the original value but mark as erroneous
                        TListTypeBean listTypeBean = LookupContainer.getItemTypeBean(issueTypeID, locale);
                        if (listTypeBean != null) {
                            allowedIssueTypeBeans.add(0, listTypeBean);
                            TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
                            Object[] arguments = new Object[] {
                                    FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_ISSUETYPE,
                                            locale),
                                    LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.projectType",
                                            locale),
                                    projectTypeBean.getLabel(), listTypeBean.getLabel() };
                            invalidFieldDefaults.put(SystemFields.INTEGER_ISSUETYPE,
                                    LocalizeUtil.getParametrizedString("admin.project.err.invalidSystemListEntry",
                                            arguments, locale));
                        }
                    }
                }
            }
            projectDefaultsTO.setDefaultIssueTypeID(issueTypeID);
        }

        //status
        Integer statusID = null;
        if (afterProjectTypeChange) {
            statusID = projectDefaultsTO.getInitialStatusID();
        } else {
            statusID = projectBean.getDefaultInitStateID();
        }
        List<ILabelBean> allowedStatusBeans = ProjectTypeItemTypeStatusAssignmentFacade.getInstance()
                .getPossibleBeans(projectTypeID, issueTypeID, statusID, locale);
        projectDefaultsTO.setStatusList(allowedStatusBeans);
        if (allowedStatusBeans != null && !allowedStatusBeans.isEmpty()) {
            if (statusID == null) {
                //either new or existing but the initial status was not yet set
                statusID = allowedStatusBeans.get(0).getObjectID();
            } else {
                //existing projectBean, status was set previously            
                boolean statusValid = foundInList(allowedStatusBeans, statusID);
                if (!statusValid) {
                    if (afterProjectTypeChange) {
                        //not found after projectType change, set the first valid one
                        statusID = allowedStatusBeans.get(0).getObjectID();
                    } else {
                        //by project loading leave the original value but mark as erroneous
                        TStateBean stateBean = LookupContainer.getStatusBean(statusID);
                        if (stateBean != null) {
                            allowedStatusBeans.add(0, stateBean);
                            TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
                            Object[] arguments = new Object[] {
                                    FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_STATE,
                                            locale),
                                    LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.projectType",
                                            locale),
                                    projectTypeBean.getLabel(), stateBean.getLabel() };
                            invalidFieldDefaults.put(SystemFields.INTEGER_STATE, LocalizeUtil.getParametrizedString(
                                    "admin.project.err.invalidSystemListEntry", arguments, locale));
                        }
                    }
                }
            }
            projectDefaultsTO.setInitialStatusID(statusID);
        }

        Map<Integer, Integer> issueTypeToInitStatusMap = new HashMap<Integer, Integer>();
        Map<Integer, List<ILabelBean>> issueTypeToStatusListMap = new HashMap<Integer, List<ILabelBean>>();
        List<TInitStateBean> initalStates = initStatusDAO.loadByProjectAndIssueTypes(projectID,
                GeneralUtils.createIntegerListFromBeanList(allowedIssueTypeBeans));
        for (ILabelBean itemTypeBean : allowedIssueTypeBeans) {
            Integer initIssueTypeID = itemTypeBean.getObjectID();//initStateBean.getListType();
            Integer initStatusID = getInitStatus(initalStates, projectID, initIssueTypeID);
            allowedStatusBeans = ProjectTypeItemTypeStatusAssignmentFacade.getInstance()
                    .getPossibleBeans(projectTypeID, initIssueTypeID, initStatusID, locale);
            //existing projectBean, status was set previously
            boolean statusValid = foundInList(allowedStatusBeans, initStatusID);
            if (!statusValid) {
                if (afterProjectTypeChange) {
                    //not found after projectType change, set the first valid one
                    initStatusID = allowedStatusBeans.get(0).getObjectID();
                    issueTypeToInitStatusMap.put(initIssueTypeID, initStatusID);
                } else {
                    //by project loading leave the original value but mark as erroneous
                    TStateBean stateBean = LookupContainer.getStatusBean(initStatusID);
                    if (stateBean != null) {
                        allowedStatusBeans.add(0, stateBean);
                        TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
                        Object[] arguments = new Object[] {
                                FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_STATE, locale),
                                LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.projectType",
                                        locale),
                                projectTypeBean.getLabel(), stateBean.getLabel() };
                        invalidFieldDefaults.put(SystemFields.INTEGER_STATE, LocalizeUtil.getParametrizedString(
                                "admin.project.err.invalidSystemListEntry", arguments, locale));
                    }
                }
            } else {
                issueTypeToInitStatusMap.put(initIssueTypeID, initStatusID);
            }
            issueTypeToStatusListMap.put(initIssueTypeID, allowedStatusBeans);
        }
        projectDefaultsTO.setIssueTypeToInitStatusMap(issueTypeToInitStatusMap);
        projectDefaultsTO.setIssueTypeToStatusListMap(issueTypeToStatusListMap);

        //priorities   
        Integer priorityID = null;
        if (afterProjectTypeChange) {
            priorityID = projectDefaultsTO.getDefaultPriorityID();
        } else {
            priorityID = PropertiesHelper.getIntegerProperty(moreProps, TProjectBean.MOREPPROPS.DEFAULT_PRIORITY);
        }
        List<ILabelBean> allowedPriorityBeans = ProjectTypeItemTypePriorityAssignmentFacade.getInstance()
                .getPossibleBeans(projectTypeID, issueTypeID, priorityID, locale);
        projectDefaultsTO.setPriorityList(allowedPriorityBeans);
        if (allowedPriorityBeans != null && !allowedPriorityBeans.isEmpty()) {
            if (priorityID == null) {
                //either new or existing but the initial priority was not yet set
                priorityID = allowedPriorityBeans.get(0).getObjectID();
            } else {
                //existing projectBean, priority was set previously            
                boolean priorityValid = foundInList(allowedPriorityBeans, priorityID);
                if (!priorityValid) {
                    if (afterProjectTypeChange) {
                        //not found after projectType change, set the first valid one
                        priorityID = allowedPriorityBeans.get(0).getObjectID();
                    } else {
                        //by project loading leave the original value but mark as erroneous
                        TPriorityBean priorityBean = LookupContainer.getPriorityBean(priorityID, locale);
                        if (priorityBean != null) {
                            allowedPriorityBeans.add(0, priorityBean);
                            TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
                            Object[] arguments = new Object[] {
                                    FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_PRIORITY,
                                            locale),
                                    LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.projectType",
                                            locale),
                                    projectTypeBean.getLabel(), priorityBean.getLabel() };
                            invalidFieldDefaults.put(SystemFields.INTEGER_PRIORITY,
                                    LocalizeUtil.getParametrizedString("admin.project.err.invalidSystemListEntry",
                                            arguments, locale));
                        }
                    }
                }
            }
            projectDefaultsTO.setDefaultPriorityID(priorityID);
        }

        //severities
        Integer severityID = null;
        if (afterProjectTypeChange) {
            severityID = projectDefaultsTO.getDefaultSeverityID();
        } else {
            severityID = PropertiesHelper.getIntegerProperty(moreProps, TProjectBean.MOREPPROPS.DEFAULT_SEVERITY);
        }
        List<ILabelBean> allowedSeverityBeans = ProjectTypeItemTypeSeverityAssignmentFacade.getInstance()
                .getPossibleBeans(projectTypeID, issueTypeID, severityID, locale);
        projectDefaultsTO.setSeverityList(allowedSeverityBeans);
        if (allowedSeverityBeans != null && !allowedSeverityBeans.isEmpty()) {
            if (severityID == null) {
                //either new or existing but the initial severity was not yet set
                severityID = allowedSeverityBeans.get(0).getObjectID();
            } else {
                //existing projectBean, severity was set previously
                boolean severityValid = foundInList(allowedSeverityBeans, severityID);
                if (!severityValid) {
                    if (afterProjectTypeChange) {
                        //not found after projectType change, set the first valid one
                        severityID = allowedSeverityBeans.get(0).getObjectID();
                    } else {
                        //by project loading leave the original value but mark as erroneous
                        TSeverityBean severityBean = LookupContainer.getSeverityBean(severityID, locale);
                        if (severityBean != null) {
                            allowedSeverityBeans.add(0, severityBean);
                            TProjectTypeBean projectTypeBean = ProjectTypesBL.loadByPrimaryKey(projectTypeID);
                            Object[] arguments = new Object[] {
                                    FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_SEVERITY,
                                            locale),
                                    LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.projectType",
                                            locale),
                                    projectTypeBean.getLabel(), severityBean.getLabel() };
                            invalidFieldDefaults.put(SystemFields.INTEGER_SEVERITY,
                                    LocalizeUtil.getParametrizedString("admin.project.err.invalidSystemListEntry",
                                            arguments, locale));
                        }
                    }
                }
            }
            projectDefaultsTO.setDefaultSeverityID(severityID);
        }
        return invalidFieldDefaults;
    }

    /**
     * Whether an entry is found in a list 
     * @return
     */
    private static Integer getInitStatus(List<TInitStateBean> initStateBeans, Integer projectID,
            Integer itemTypeID) {
        if (initStateBeans != null && projectID != null && itemTypeID != null) {
            for (TInitStateBean initStateBean : initStateBeans) {
                Integer assignedProjectID = initStateBean.getProject();
                Integer assignedItemTypeID = initStateBean.getListType();
                Integer assignedInitStatusID = initStateBean.getStateKey();
                if (projectID.equals(assignedProjectID) && itemTypeID.equals(assignedItemTypeID)) {
                    return assignedInitStatusID;
                }
            }
        }
        return null;
    }

    /**
     * Validates the user entered project details
     * @param projectID
     * @param add
     * @param addAsSubproject
     * @param projectBaseTO
     * @param locale
     * @return
     */
    static List<ControlError> validateProjectDetail(Integer projectID, boolean add, boolean addAsSubproject,
            ProjectBaseTO projectBaseTO, Locale locale) {
        List<ControlError> errors = new ArrayList<ControlError>();
        Integer projectStatusNew = projectBaseTO.getProjectStatusID();
        Integer statusFlagNew = SystemStatusBL.getStatusFlagForStatusID(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE,
                projectStatusNew);
        boolean projectNameExists = false;
        boolean projectPrefixExists = false;
        TProjectBean projectBean;
        if (add) {
            projectBean = new TProjectBean();
            if (addAsSubproject) {
                validateChildStatus(add, statusFlagNew, projectID, locale, errors);
                projectNameExists = ProjectBL.projectNameExists(projectBaseTO.getLabel(), projectID, null);
            } else {
                projectNameExists = ProjectBL.projectNameExists(projectBaseTO.getLabel(), null, null);
            }
        } else {
            projectBean = ProjectBL.loadByPrimaryKey(projectID);
            if (projectBean == null) {
                projectBean = new TProjectBean();
            }
            //validateChildStatus(add, statusFlagNew, projectBean.getParent(), locale, errors);
            projectNameExists = ProjectBL.projectNameExists(projectBaseTO.getLabel(), projectBean.getParent(),
                    projectID);
            if (ApplicationBean.getInstance().getSiteBean().getProjectSpecificIDsOn()) {
                String prefix = projectBaseTO.getPrefix();
                if (prefix != null && !"".equals(prefix)) {
                    projectPrefixExists = ProjectBL.projectPrefixExists(prefix, projectBean.getParent(), projectID);
                }
            }
        }
        if (projectNameExists) {
            List<String> controlPath = new LinkedList<String>();
            controlPath.add("mainTab");
            controlPath.add("fsbasic");
            controlPath.add("projectBaseTO.label");
            errors.add(new ControlError(controlPath, LocalizeUtil.getParametrizedString("common.err.duplicateName",
                    new Object[] {
                            FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_PROJECT, locale) },
                    locale)));
        }
        if (projectPrefixExists) {
            List<String> controlPath = new LinkedList<String>();
            controlPath.add("mainTab");
            controlPath.add("fsprefix");
            controlPath.addAll(JSONUtility.getPathInHelpWrapper("projectBaseTO.prefix"));
            errors.add(new ControlError(controlPath, LocalizeUtil.getParametrizedString(
                    "common.err.duplicatePrefix",
                    new Object[] {
                            FieldRuntimeBL.getLocalizedDefaultFieldLabel(SystemFields.INTEGER_PROJECT, locale) },
                    locale)));
        }
        return errors;
    }

    /**
     * Validates the child status depending on parent status
     * @param add
     * @param statusFlagChild
     * @param parentProjectID
     * @param locale
     * @param errors
     */
    private static void validateChildStatus(boolean add, Integer statusFlagChild, Integer parentProjectID,
            Locale locale, List<ControlError> errors) {
        if (parentProjectID != null && statusFlagChild != null
                && statusFlagChild.intValue() != TSystemStateBean.STATEFLAGS.CLOSED) {
            TProjectBean parentProject = ProjectBL.loadByPrimaryKey(parentProjectID);
            if (parentProject != null) {
                Integer parentProjectStatus = parentProject.getStatus();
                if (parentProjectStatus != null) {
                    Integer parentStatusFlag = SystemStatusBL.getStatusFlagForStatusID(
                            TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, parentProjectStatus);
                    if (parentStatusFlag != null) {
                        if (parentStatusFlag.intValue() == TSystemStateBean.STATEFLAGS.CLOSED) {
                            List<String> controlPath = new LinkedList<String>();
                            controlPath.add("mainTab");
                            controlPath.add("fsbasic");
                            controlPath.add("projectStatus");
                            String errorKey = null;
                            if (add) {
                                errorKey = "admin.project.err.addOpenChildToClosedParent";
                            } else {
                                errorKey = "admin.project.err.closedDescendant";
                            }
                            errors.add(new ControlError(controlPath,
                                    LocalizeUtil.getLocalizedTextFromApplicationResources(errorKey, locale)));
                        } else {
                            if (parentStatusFlag.intValue() == TSystemStateBean.STATEFLAGS.INACTIVE
                                    && statusFlagChild.intValue() == TSystemStateBean.STATEFLAGS.ACTIVE) {
                                List<String> controlPath = new LinkedList<String>();
                                controlPath.add("mainTab");
                                controlPath.add("fsbasic");
                                controlPath.add("projectStatus");
                                String errorKey = null;
                                if (add) {
                                    errorKey = "admin.project.err.addActiveChildToInactiveParent";
                                } else {
                                    errorKey = "admin.project.err.activChildInInactiveParent";
                                }
                                errors.add(new ControlError(controlPath,
                                        LocalizeUtil.getLocalizedTextFromApplicationResources(errorKey, locale)));
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Validates the child status depending on parent status
     */
    private static String validateStatus(boolean confirmSave, TProjectBean projectBean, Integer projectStatusOld,
            Integer projectStatusNew, Locale locale) {
        if (projectStatusOld != null && projectStatusNew != null && !projectStatusOld.equals(projectStatusNew)) {
            Integer statusFlagOld = SystemStatusBL
                    .getStatusFlagForStatusID(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, projectStatusOld);
            Integer statusFlagNew = SystemStatusBL
                    .getStatusFlagForStatusID(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, projectStatusNew);
            if (statusFlagOld != null && statusFlagNew != null
                    && EqualUtils.notEqual(statusFlagOld, statusFlagNew)) {
                boolean notClosedToClosed = statusFlagOld.intValue() != TSystemStateBean.STATEFLAGS.CLOSED
                        && statusFlagNew.intValue() == TSystemStateBean.STATEFLAGS.CLOSED;
                boolean activeToInactive = statusFlagOld.intValue() == TSystemStateBean.STATEFLAGS.ACTIVE
                        && statusFlagNew.intValue() == TSystemStateBean.STATEFLAGS.INACTIVE;
                boolean closedToNotClosed = statusFlagOld.intValue() == TSystemStateBean.STATEFLAGS.CLOSED
                        && statusFlagNew.intValue() != TSystemStateBean.STATEFLAGS.CLOSED;
                boolean inactiveToActive = statusFlagOld.intValue() == TSystemStateBean.STATEFLAGS.INACTIVE
                        && statusFlagNew.intValue() == TSystemStateBean.STATEFLAGS.ACTIVE;
                Integer projectID = projectBean.getObjectID();
                if (notClosedToClosed || activeToInactive) {
                    List<TProjectBean> inconsistenSubrojects = null;
                    if (projectID != null) {
                        if (notClosedToClosed) {
                            inconsistenSubrojects = ProjectBL.loadActiveInactiveSubrojects(projectID);
                        } else {
                            inconsistenSubrojects = ProjectBL.loadActiveSubrojects(projectID);
                        }
                    }
                    if (inconsistenSubrojects != null && !inconsistenSubrojects.isEmpty()) {
                        if (confirmSave) {
                            for (TProjectBean childProjectBean : inconsistenSubrojects) {
                                changeDescendantsStatus(childProjectBean, activeToInactive, projectStatusNew);
                            }
                        } else {
                            String titleKey = null;
                            String errorReasonKey = null;
                            String errorResolutionKey = null;
                            if (notClosedToClosed) {
                                titleKey = "admin.project.err.closeOpenDescendantTitle";
                                errorReasonKey = "admin.project.err.closedDescendant";
                                errorResolutionKey = "admin.project.err.closeOpenDescendant";
                            } else {
                                titleKey = "admin.project.err.deactivateActivDescendantTitle";
                                errorReasonKey = "admin.project.err.activChildInInactiveParent";
                                errorResolutionKey = "admin.project.err.deactivateActivDescendant";
                            }
                            return ReleaseJSON.saveFailureJSON(JSONUtility.EDIT_ERROR_CODES.NEED_CONFIRMATION,
                                    LocalizeUtil.getLocalizedTextFromApplicationResources(titleKey, locale),
                                    LocalizeUtil.getLocalizedTextFromApplicationResources(errorReasonKey, locale)
                                            + " " + LocalizeUtil.getLocalizedTextFromApplicationResources(
                                                    errorResolutionKey, locale));
                        }
                    }
                } else {
                    if (closedToNotClosed || inactiveToActive) {
                        Integer parentProjectID = projectBean.getParent();
                        if (parentProjectID != null) {
                            TProjectBean parentProjectBean = ProjectBL.loadByPrimaryKey(parentProjectID);
                            if (parentProjectBean != null) {
                                Integer projectStatusParent = parentProjectBean.getStatus();
                                if (projectStatusParent != null) {
                                    Integer parentStatusFlag = SystemStatusBL.getStatusFlagForStatusID(
                                            TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, projectStatusParent);
                                    boolean inconsistentParent = false;
                                    if (closedToNotClosed) {
                                        inconsistentParent = parentStatusFlag
                                                .intValue() == TSystemStateBean.STATEFLAGS.CLOSED;
                                    } else {
                                        inconsistentParent = parentStatusFlag
                                                .intValue() == TSystemStateBean.STATEFLAGS.INACTIVE;
                                    }
                                    if (inconsistentParent) {
                                        if (confirmSave) {
                                            changeAscendentsStatus(parentProjectBean, inactiveToActive,
                                                    projectStatusNew);
                                        } else {
                                            String titleKey = null;
                                            String errorReasonKey = null;
                                            String errorResolutionKey = null;
                                            if (closedToNotClosed) {
                                                titleKey = "admin.project.err.openClosedAncestorsTitle";
                                                errorReasonKey = "admin.project.err.closedDescendant";
                                                errorResolutionKey = "admin.project.err.openClosedAncestors";
                                            } else {
                                                titleKey = "admin.project.err.activateInactiveAscendentTitle";
                                                errorReasonKey = "admin.project.err.activChildInInactiveParent";
                                                errorResolutionKey = "admin.project.err.activateInactiveAscendent";
                                            }
                                            return ReleaseJSON.saveFailureJSON(
                                                    JSONUtility.EDIT_ERROR_CODES.NEED_CONFIRMATION,
                                                    LocalizeUtil.getLocalizedTextFromApplicationResources(titleKey,
                                                            locale),
                                                    LocalizeUtil.getLocalizedTextFromApplicationResources(
                                                            errorReasonKey, locale) + " "
                                                            + LocalizeUtil.getLocalizedTextFromApplicationResources(
                                                                    errorResolutionKey, locale));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Saves a new/existing project
     * @param projectID
     * @param add
     * @param addAsSubproject
     * @param projectBaseTO
     * @param projectDefaultsTO
     * @param confirmSave
     * @param locale
     * @return
     */
    static String saveProjectDetail(Integer projectID, boolean add, boolean addAsSubproject,
            boolean addAsPrivateProject, ProjectBaseTO projectBaseTO, ProjectAccountingTO projectAccountingTO,
            ProjectDefaultsTO projectDefaultsTO, boolean confirmSave, boolean isTemplate, Integer personID,
            Locale locale) {
        TProjectBean projectBean = null;
        //whether the root or a branch should be reloaded after project save
        boolean reloadProjectTree = false;
        boolean prefixChanged = false;
        //if null (and reloadTree is true) reload the root node
        String projectNodeToReload = null;
        //which project to select after save: set only if reloadTree==true
        //if this is not null a reload is made in the client 
        String projectNodeToSelect = null;
        Integer projectStatusOld = null;
        Integer projectStatusNew = projectBaseTO.getProjectStatusID();
        if (add) {
            reloadProjectTree = true;
            //prefixChanged = true;
            //projectTypeChanged = true;
            projectBean = new TProjectBean();
            if (addAsSubproject) {
                if (projectID != null) {
                    projectBean.setParent(projectID);
                    projectNodeToReload = projectID.toString();
                }
            }
            if (addAsPrivateProject) {
                projectBean.setPrivate(addAsPrivateProject);
            }
        } else {
            projectBean = ProjectBL.loadByPrimaryKey(projectID);
            if (projectBean == null) {
                projectBean = new TProjectBean();
            }
            prefixChanged = EqualUtils.notEqual(projectBean.getPrefix(), projectBaseTO.getPrefix());
            if (EqualUtils.notEqual(projectBean.getLabel(), projectBaseTO.getLabel())
                    || EqualUtils.notEqual(projectBean.getStatus(), projectBaseTO.getProjectStatusID())) {
                //either name or project status changed: change the node in the projects tree: name or icon
                reloadProjectTree = true;
                Integer parentProjectID = projectBean.getParent();
                if (parentProjectID != null) {
                    projectNodeToReload = parentProjectID.toString();
                }
            }
            projectStatusOld = projectBean.getStatus();
            //test the parent statuses
            String needReplaceMessage = validateStatus(confirmSave, projectBean, projectStatusOld, projectStatusNew,
                    locale);
            if (needReplaceMessage != null) {
                return needReplaceMessage;
            }
        }
        //projectBase
        projectBean.setLabel(projectBaseTO.getLabel());
        projectBean.setDescription(projectBaseTO.getDescription());
        Integer newProjectType = projectBaseTO.getProjectTypeID();
        Integer oldProjectType = projectBean.getProjectType();
        projectBean.setProjectType(newProjectType);
        if (newProjectType < 0) {
            projectBean.setPrivate(true);
        }
        projectBean.setStatus(projectBaseTO.getProjectStatusID());
        if (projectBaseTO.getProjectStatusID() == null && add && isTemplate) {
            projectBean.setStatus(ProjectCopyBL.getProjectStatusActive());
        }

        Properties properties = PropertiesHelper.getProperties(projectBean.getMoreProps());
        PropertiesHelper.setBooleanProperty(properties, TProjectBean.MOREPPROPS.LINKING_PROPERTY,
                projectBaseTO.isLinking());
        boolean newUseRelease = projectBaseTO.isUseRelease();
        boolean oldUseRelease = PropertiesHelper.getBooleanProperty(projectBean.getMoreProps(),
                TProjectBean.MOREPPROPS.USE_RELEASE);
        //whether to show release/account/version control nodes
        boolean reloadProjectConfigTree = newUseRelease != oldUseRelease
                || EqualUtils.notEqual(newProjectType, oldProjectType);
        String projectConfigTypeNodeToSelect = String.valueOf(ProjectConfigBL.PROJECT_ASSIGNMENTS.GENERAL);
        PropertiesHelper.setBooleanProperty(properties, TProjectBean.MOREPPROPS.USE_RELEASE, newUseRelease);
        projectBean.setPrefix(projectBaseTO.getPrefix());
        //project accounting
        if (projectAccountingTO == null) {
            projectAccountingTO = new ProjectAccountingTO();
        }
        PropertiesHelper.setBooleanProperty(properties, TProjectBean.MOREPPROPS.ACCOUNTING_INHERITED,
                projectAccountingTO.isAccountingInherited());
        boolean oldWorkTracking = PropertiesHelper.getBooleanProperty(properties,
                TProjectBean.MOREPPROPS.WORK_TRACKING);
        boolean newWorkTracking = projectAccountingTO.isWorkTracking();
        PropertiesHelper.setBooleanProperty(properties, TProjectBean.MOREPPROPS.WORK_TRACKING, newWorkTracking);
        PropertiesHelper.setIntegerProperty(properties, TProjectBean.MOREPPROPS.WORK_DEFAULT_UNIT,
                projectAccountingTO.getDefaultWorkUnit());
        projectBean.setHoursPerWorkDay(projectAccountingTO.getHoursPerWorkday());
        boolean oldCostTracking = PropertiesHelper.getBooleanProperty(properties,
                TProjectBean.MOREPPROPS.COST_TRACKING);
        boolean newCostTracking = projectAccountingTO.isCostTracking();
        PropertiesHelper.setBooleanProperty(properties, TProjectBean.MOREPPROPS.COST_TRACKING, newCostTracking);
        projectBean.setCurrencyName(projectAccountingTO.getCurrencyName());
        projectBean.setCurrencySymbol(projectAccountingTO.getCurrencySymbol());
        PropertiesHelper.setIntegerProperty(properties, TProjectBean.MOREPPROPS.DEFAULT_ACCOUNT,
                projectAccountingTO.getDefaultAccount());
        if (!reloadProjectConfigTree) {
            reloadProjectConfigTree = (oldWorkTracking || oldCostTracking) != (newWorkTracking || newCostTracking);
        }

        //project defaults
        projectBean.setDefaultManagerID(projectDefaultsTO.getDefaultManagerID());
        projectBean.setDefaultOwnerID(projectDefaultsTO.getDefaultResponsibleID());
        projectBean.setDefaultInitStateID(projectDefaultsTO.getInitialStatusID());
        projectBean.setIsTemplate(BooleanFields.fromBooleanToString(isTemplate));
        PropertiesHelper.setIntegerProperty(properties, TProjectBean.MOREPPROPS.DEFAULT_ISSUETYPE,
                projectDefaultsTO.getDefaultIssueTypeID());
        PropertiesHelper.setIntegerProperty(properties, TProjectBean.MOREPPROPS.DEFAULT_PRIORITY,
                projectDefaultsTO.getDefaultPriorityID());
        PropertiesHelper.setIntegerProperty(properties, TProjectBean.MOREPPROPS.DEFAULT_SEVERITY,
                projectDefaultsTO.getDefaultSeverityID());
        PropertiesHelper.setIntegerProperty(properties, TProjectBean.MOREPPROPS.PREFILL,
                projectDefaultsTO.getPrefillBy());

        projectBean.setMoreProps(PropertiesHelper.serializeProperties(properties));

        projectID = ProjectBL.save(projectBean);
        if (prefixChanged) {
            //update the lucene index for workItems from project if prefix was changed
            int[] workItemIDsArr = workItemDAO.loadBySystemList(SystemFields.PROJECT, projectID);
            LuceneIndexer.updateWorkItemIndex(workItemIDsArr, SystemFields.INTEGER_PROJECT);
            ClusterMarkChangesBL.markDirtyWorkItemsInCluster(workItemIDsArr);
        }
        if (projectID != null) {
            //should be specified only if reload of branch is needed
            projectNodeToSelect = projectID.toString();
        }

        /**
         * Issue type specific initial statuses
         */
        Map<Integer, Integer> issueTypeToInitStatusMap = projectDefaultsTO.getIssueTypeToInitStatusMap();
        if (issueTypeToInitStatusMap == null) {
            issueTypeToInitStatusMap = new HashMap<Integer, Integer>();
        }
        Map<Integer, Integer> issueTypeNegativeToInitStatusMap = projectDefaultsTO
                .getIssueTypeNegativeToInitStatusMap();
        for (Integer issueTypeID : issueTypeNegativeToInitStatusMap.keySet()) {
            //turn back to negative the values turned at positive on the client (due to struts2 bug with negative numbers)
            issueTypeToInitStatusMap.put(Integer.valueOf(-issueTypeID.intValue()),
                    issueTypeNegativeToInitStatusMap.get(issueTypeID));
        }

        //get the existing ones
        List<TInitStateBean> initStates = initStatusDAO.loadByProject(projectID);
        for (TInitStateBean initStateBean : initStates) {
            Integer existingIssueTypeID = initStateBean.getListType();
            Integer existingStatus = initStateBean.getStateKey();
            if (issueTypeToInitStatusMap.containsKey(existingIssueTypeID)) {
                Integer newStatus = issueTypeToInitStatusMap.get(existingIssueTypeID);
                //issue type specific initial status changed
                if (EqualUtils.notEqual(existingStatus, newStatus)) {
                    initStateBean.setStateKey(newStatus);
                    initStatusDAO.save(initStateBean);
                }
                issueTypeToInitStatusMap.remove(existingIssueTypeID);
            } else {
                //issue type specific initial status removed
                initStatusDAO.delete(initStateBean.getObjectID());
            }
        }
        //issue type specific initial status added
        for (Integer issueTypeID : issueTypeToInitStatusMap.keySet()) {
            Integer statusID = issueTypeToInitStatusMap.get(issueTypeID);
            TInitStateBean initStateBean = new TInitStateBean();
            initStateBean.setListType(issueTypeID);
            initStateBean.setStateKey(statusID);
            initStateBean.setProject(projectID);
            initStatusDAO.save(initStateBean);
        }

        if (addAsPrivateProject) {
            //add full rights to the private project for the actual user by adding the negative role to the person in project
            List<TRoleBean> roleBeans = RoleBL.loadNotVisible();
            if (roleBeans != null && !roleBeans.isEmpty()) {
                List<Integer> personIDs = new LinkedList<Integer>();
                personIDs.add(personID);
                RoleAssignmentsBL.addPersonsToRoles(projectID, roleBeans.get(0).getObjectID(), personIDs);
            }
        }
        return ProjectJSON.saveProjectDetailJSON(projectNodeToReload, projectNodeToSelect, reloadProjectTree,
                projectConfigTypeNodeToSelect, reloadProjectConfigTree);
    }

    /**
     * Close the not closed descendants
     * @param projectBean
     * @param onlyActive
     * @param newStatusID
     * @return
     */
    private static void changeDescendantsStatus(TProjectBean projectBean, boolean onlyActive, Integer newStatusID) {
        List<TProjectBean> childProjects = null;
        if (onlyActive) {
            childProjects = ProjectBL.loadActiveSubrojects(projectBean.getObjectID());
        } else {
            childProjects = ProjectBL.loadActiveInactiveSubrojects(projectBean.getObjectID());
        }
        if (childProjects != null) {
            for (TProjectBean childProjectBean : childProjects) {
                changeDescendantsStatus(childProjectBean, onlyActive, newStatusID);
            }
        }
        projectBean.setStatus(newStatusID);
        ProjectBL.saveSimple(projectBean);
    }

    /**
     * Open the closed assignments up to the first opened ascendent
     * @param projectBean
     * @param activateAscendant
     * @param newStatusID
     */
    private static void changeAscendentsStatus(TProjectBean projectBean, boolean activateAscendant,
            Integer newStatusID) {
        if (projectBean != null) {
            Integer projectStatus = projectBean.getStatus();
            Integer statusFlag = SystemStatusBL.getStatusFlagForStatusID(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE,
                    projectStatus);
            if (statusFlag != null) {
                boolean changeAscendent = false;
                if (activateAscendant) {
                    changeAscendent = statusFlag.intValue() == TSystemStateBean.STATEFLAGS.INACTIVE;
                } else {
                    changeAscendent = statusFlag.intValue() == TSystemStateBean.STATEFLAGS.CLOSED;
                }
                if (changeAscendent) {
                    projectBean.setStatus(newStatusID);
                    ProjectBL.saveSimple(projectBean);
                    Integer parentID = projectBean.getParent();
                    if (parentID != null) {
                        TProjectBean parentProjectBean = ProjectBL.loadByPrimaryKey(parentID);
                        changeAscendentsStatus(parentProjectBean, activateAscendant, newStatusID);
                    }
                }
            }
        }
    }

    /**
     * Clears the parent for a project
     * @param projectID
     * @return
     */
    static boolean clearParent(Integer projectID) {
        TProjectBean projectBean = ProjectBL.loadByPrimaryKey(projectID);
        if (projectBean != null) {
            projectBean.setParent(null);
            ProjectBL.saveSimple(projectBean);
            return true;
        }
        return false;
    }

    /**
     * Updates the parent for a project
     * @param projectID
     * @param parentID
     * @return
     */
    static boolean updateParent(Integer projectID, Integer parentID) {
        TProjectBean projectBean = ProjectBL.loadByPrimaryKey(projectID);
        if (projectBean != null) {
            if (parentID != null) {
                TProjectBean parent = ProjectBL.loadByPrimaryKey(parentID);
                if (parent != null) {
                    projectBean.setParent(parentID);
                    ProjectBL.saveSimple(projectBean);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Deletes a project with all subprojects and issues
     * @param projectID
     * @param deleteConfirmed
     * @param personBean
     * @param locale
     * @return
     */
    public static String deleteProject(Integer projectID, boolean deleteConfirmed, TPersonBean personBean,
            Locale locale) {
        String errorMessage;
        if (!personBean.isSys()) {
            //false to do not allow to delete a project as substitute
            boolean isProjectAdmin = AccessBeans.isPersonProjectAdminForProject(personBean.getObjectID(), projectID,
                    false);
            if (!isProjectAdmin) {
                errorMessage = LocalizeUtil.getParametrizedString("common.lbl.noDeleteRight",
                        new Object[] { LocalizeUtil
                                .getLocalizedTextFromApplicationResources("admin.project.lbl.project", locale) },
                        locale);
                LOGGER.warn("User " + personBean.getLoginName() + " tried to delete the project " + projectID
                        + " but has no delete right");
                return JSONUtility.encodeJSONFailure(errorMessage,
                        JSONUtility.DELETE_ERROR_CODES.NO_RIGHT_TO_DELETE);
            }
        }
        if (!deleteConfirmed && hasDependentIssues(projectID)) {
            errorMessage = LocalizeUtil.getLocalizedTextFromApplicationResources("admin.project.err.hasIssues",
                    locale);
            errorMessage = errorMessage + " "
                    + LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.cancelDeleteAlert", locale);
            return JSONUtility.encodeJSONFailure(errorMessage, JSONUtility.DELETE_ERROR_CODES.NOT_EMPTY_WARNING);
        }
        if (!deleteConfirmed && hasSubprojects(projectID)) {
            errorMessage = LocalizeUtil.getLocalizedTextFromApplicationResources("admin.project.err.hasSubprojects",
                    locale);
            errorMessage = errorMessage
                    + LocalizeUtil.getLocalizedTextFromApplicationResources("common.lbl.cancelDeleteAlert", locale);
            return JSONUtility.encodeJSONFailure(errorMessage, JSONUtility.DELETE_ERROR_CODES.NOT_EMPTY_WARNING);
        }
        String nodeToReload = null;
        TProjectBean projectBean = ProjectBL.loadByPrimaryKey(projectID);
        if (projectBean != null) {
            Integer parentProjectId = projectBean.getParent();
            if (parentProjectId != null) {
                nodeToReload = parentProjectId.toString();
            }
            LOGGER.fatal("Deleting the project " + projectBean.getLabel() + " with ID " + projectID + " by "
                    + personBean.getLoginName() + " at " + new Date());
            deleteRecursively(projectBean, personBean);
        }
        //force reloading of projects only after all projects are deleted
        LookupContainer.resetProjectMap();
        return ProjectJSON.saveDeleteDetailJSON(nodeToReload, nodeToReload, projectID, true);
    }

    /**
     * Deletes all subprojects and then the project
     * @param projectBean
     */
    private static void deleteRecursively(TProjectBean projectBean, TPersonBean personBean) {
        Integer projectID = projectBean.getObjectID();
        List<TProjectBean> subprojectBeans = ProjectBL.loadSubrojects(projectID);
        if (subprojectBeans != null) {
            for (TProjectBean subprojectBean : subprojectBeans) {
                LOGGER.fatal("Deleting the subproject " + subprojectBean.getLabel() + " with ID "
                        + subprojectBean.getObjectID() + " parent ID " + projectID + " by "
                        + personBean.getLoginName() + " at " + new Date());
                deleteRecursively(subprojectBean, personBean);
            }
        }
        deleteProject(projectBean);
    }

    /**
     * Deletes a project with all subprojects and issues
     * @param projectBean
     */
    private static void deleteProject(TProjectBean projectBean) {
        if (projectBean != null) {
            Integer projectID = projectBean.getObjectID();
            LOGGER.fatal("Project " + projectBean.getLabel() + " with ID " + projectID + " is now deleted...");
            //delete all the workItems contained in this project from the corresponding lucene indexes
            List<TWorkItemBean> workItemBeans = ItemBL.loadAllByProject(projectID,
                    Integer.valueOf(FilterUpperTO.ARCHIVED_FILTER.INCLUDE_ARCHIVED),
                    Integer.valueOf(FilterUpperTO.DELETED_FILTER.INCLUDE_DELETED));
            if (workItemBeans != null) {
                LOGGER.fatal("Number of items to delete from project " + projectBean.getLabel() + " with ID "
                        + projectID + " is " + workItemBeans.size());
            }
            List<Integer> workItemIDs = GeneralUtils.createIntegerListFromBeanList(workItemBeans);
            //delete the workItem index entries for this list of workItems
            LuceneIndexer.deleteFromWorkItemIndex(workItemBeans, SystemFields.INTEGER_PROJECT, true);
            //delete from lucene indexes
            List<IAssociatedFieldsIndexer> associatedFieldIndexers = LuceneIndexer.getAssociatedFieldIndexers();
            for (IAssociatedFieldsIndexer associatedFieldsIndexer : associatedFieldIndexers) {
                associatedFieldsIndexer.deleteByWorkItems(workItemIDs);
            }
            List<TExportTemplateBean> projectReports = ReportBL.loadProject(projectID);
            if (projectReports != null) {
                for (TExportTemplateBean exportTemplateBean : projectReports) {
                    ReportBL.deleteWithTemplate(exportTemplateBean.getObjectID());
                }
            }

            //delete all his workItems and other direct (foreign key) dependencies.
            projectDAO.deleteDependencies(projectID);

            /**
             * Delete the project and release specific dashboards
             */
            /*DashboardScreenDAO dashboardScreenDAO = DAOFactory.getFactory().getDashboardScreenDAO();
            TDashboardScreenBean projectDashboardScreenBean = dashboardScreenDAO.loadByProject(projectID, SystemFields.INTEGER_PROJECT);
            if (projectDashboardScreenBean!=null) {
               dashboardScreenDAO.delete(projectDashboardScreenBean.getObjectID());
            }
            TDashboardScreenBean releaseDashboardScreenBean = dashboardScreenDAO.loadByProject(projectID, SystemFields.INTEGER_RELEASE);
            if (releaseDashboardScreenBean!=null) {
               dashboardScreenDAO.delete(releaseDashboardScreenBean.getObjectID());
            }*/

            //the project related lists cannot be deleted easily, just through business logic
            //by now the project's workItems are deleted, also the TOptionSettings related to 
            //this project (through deleting the TFieldConfigs) by the reflection code above.
            //now try to delete the lists (and also the child lists) and the entries from the list (and child lists)
            List<TListBean> listBeanList = ListBL.getListsByProject(projectID, false);
            if (listBeanList != null) {
                Iterator<TListBean> iterator = listBeanList.iterator();
                while (iterator.hasNext()) {
                    TListBean listBean = iterator.next();
                    CustomListBL.getInstance().delete(listBean.getObjectID());
                }
            }
            //if some lists can't be deleted for some reason, then set the project field to null,
            //then at least the project itself can be deleted. 
            listBeanList = ListBL.getListsByProject(projectID, false);
            if (listBeanList != null) {
                Iterator<TListBean> iterator = listBeanList.iterator();
                while (iterator.hasNext()) {
                    TListBean listBean = iterator.next();
                    listBean.setDeleted(BooleanFields.TRUE_VALUE);
                    listBean.setProject(null);
                    ListBL.save(listBean);
                }
            }

            /**
             * Remove the occurrences of project in project pickers
             */
            List<Integer> projectPickerFields = FieldBL.getCustomPickerFieldIDs(SystemFields.INTEGER_PROJECT);
            if (projectPickerFields != null && !projectPickerFields.isEmpty()) {
                for (Integer fieldID : projectPickerFields) {
                    AttributeValueBL.deleteBySystemOption(fieldID, projectID, SystemFields.INTEGER_PROJECT);
                }
            }
            /**
             * Set the occurrences in history to null instead of removing, because then the history would not have any clue about removed project from project picker
             */
            FieldChangeBL.setSystemOptionToNullInHistory(projectID, SystemFields.INTEGER_PROJECT, true);
            FieldChangeBL.setSystemOptionToNullInHistory(projectID, SystemFields.INTEGER_PROJECT, false);

            LastExecutedBL.deleteByFilterIDAndFilterType(-projectID, QUERY_TYPE.PROJECT_RELEASE);
            //now hopefully the project has no foreign key dependences and can be deleted without problem
            projectDAO.delete(projectID);
            //delete from lucene index
            NotLocalizedListIndexer.getInstance().deleteByKeyAndType(projectID,
                    LuceneUtil.LOOKUPENTITYTYPES.PROJECT);
            //cache and possible lucene update in other nodes
            ClusterMarkChangesBL.markDirtySystemListEntryInCluster(SystemFields.INTEGER_PROJECT, projectID,
                    ClusterMarkChangesBL.getChangeTypeByDelete());
        }
    }

    /**
     * @param projectID primary key of a project
     * @return true, if project has workItems
     */
    private static boolean hasDependentIssues(Integer projectID) {
        List<TWorkItemBean> workItemBeans = ItemBL.loadAllByProject(projectID,
                Integer.valueOf(FilterUpperTO.ARCHIVED_FILTER.INCLUDE_ARCHIVED),
                Integer.valueOf(FilterUpperTO.DELETED_FILTER.INCLUDE_DELETED));
        return (workItemBeans != null) && (workItemBeans.size() > 0);
    }

    /**
     * @param projectID primary key of a project
     * @return true, if project has workItems
     */
    private static boolean hasSubprojects(Integer projectID) {
        List<TProjectBean> projectBeans = ProjectBL.loadSubrojects(projectID);
        return (projectBeans != null) && (projectBeans.size() > 0);
    }

    /**
     * Whether an entry is found in a list 
     * @param labelBeans
     * @param objectID
     * @return
     */
    static boolean foundInList(List<ILabelBean> labelBeans, Integer objectID) {
        if (objectID != null && labelBeans != null) {
            for (ILabelBean labelBean : labelBeans) {
                if (labelBean.getObjectID().equals(objectID)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Whether the project or projectType might have releases
     * @param projectTypeBean
     * @param projectBean
     * @return
     */
    public static boolean mightHaveRelease(TProjectTypeBean projectTypeBean, TProjectBean projectBean) {
        return projectTypeBean != null && projectTypeBean.getUseReleases() && projectHasRelease(projectBean);
    }

    /**
     * Whether the project might have releases
     * @param projectBean
     * @return
     */
    private static boolean projectHasRelease(TProjectBean projectBean) {
        if (projectBean != null) {
            return PropertiesHelper.getBooleanProperty(projectBean.getMoreProps(),
                    TProjectBean.MOREPPROPS.USE_RELEASE);
        }
        return false;
    }

    /**
     * Whether the project or projectType might have releases
     * @param projectTypeBean
     * @param projectBean
     * @return
     */
    private static boolean mightHaveAccounting(TProjectTypeBean projectTypeBean, TProjectBean projectBean) {
        return projectTypeBean != null && projectTypeBean.getUseAccounting() && projectHasAccounting(projectBean);
    }

    /**
     * Whether the project might have releases
     * @param projectBean
     * @return
     */
    private static boolean projectHasAccounting(TProjectBean projectBean) {
        if (projectBean != null) {
            return projectBean.isWokTrackingActive() || projectBean.isCostTrackingActive();
        }
        return false;
    }

    /**
     * Get default subproject for project
     * @deprecated only used for migration
     * @param projectBean
     * @return
     */
    public static Integer getDefaultSubproject(TProjectBean projectBean) {
        Integer projectID = projectBean.getObjectID();
        String strDefaultSubproject = PropertiesHelper.getProperty(projectBean.getMoreProperties(),
                TProjectBean.MOREPPROPS.DEFAULT_SUBPROJECT);
        //Integer defaultSubproject = null;
        if (strDefaultSubproject != null && !"".equals(strDefaultSubproject)) {
            try {
                return Integer.valueOf(strDefaultSubproject);
            } catch (Exception e) {
                LOGGER.warn("Converting the default subproject " + strDefaultSubproject + " for project "
                        + projectID + " to integer failed with " + e.getMessage());
                LOGGER.debug(ExceptionUtils.getStackTrace(e));
            }
        }
        return null;
        //true because subproject is required
        //return getDefaultIfSpecifiedAndExists(defaultSubproject, subprojectDAO.loadByProject1(projectID), true);
    }

    /**
     * Get default class for project
     * @deprecated only used for migration
     * @param projectBean
     * @return
     */
    public static Integer getDefaultClass(TProjectBean projectBean) {
        Integer projectID = projectBean.getObjectID();
        String strDefaultClass = PropertiesHelper.getProperty(projectBean.getMoreProperties(),
                TProjectBean.MOREPPROPS.DEFAULT_CLASS);
        //Integer defaultClass = null;
        if (strDefaultClass != null && !"".equals(strDefaultClass)) {
            try {
                return Integer.valueOf(strDefaultClass);
            } catch (Exception e) {
                LOGGER.warn("Converting the default class " + strDefaultClass + " for project " + projectID
                        + " to integer failed with " + e.getMessage());
                LOGGER.debug(ExceptionUtils.getStackTrace(e));
            }
        }
        return null;
    }

    /**
     * Get default release for project
     * @param projectBean
     * @param noticed release noticed or scheduled
     * @return
     */
    static Integer getDefaultRelease(TProjectBean projectBean, boolean noticed, TFieldConfigBean fieldConfigBean) {
        Integer projectID = projectBean.getObjectID();
        String morePropsKey;
        List<TReleaseBean> releases = null;
        if (noticed) {
            morePropsKey = TReleaseBean.MOREPPROPS.DEFAULT_RELEASENOTICED;
            releases = ReleaseBL.loadActiveInactiveByProject(projectID);
        } else {
            morePropsKey = TReleaseBean.MOREPPROPS.DEFAULT_RELEASESCHEDULED;
            releases = ReleaseBL.loadActiveOrNotPlannedByProject(projectID);
        }
        if (releases != null && !releases.isEmpty()) {
            Iterator<TReleaseBean> iterator = releases.iterator();
            while (iterator.hasNext()) {
                TReleaseBean releaseBean = iterator.next();
                String strDefaulRelease = PropertiesHelper.getProperty(releaseBean.getMoreProperties(),
                        morePropsKey);
                if (strDefaulRelease != null && Boolean.TRUE.toString().equals(strDefaulRelease)) {
                    return releaseBean.getObjectID();
                }
            }
            if (fieldConfigBean != null && fieldConfigBean.isRequiredString()) {
                //none of them is default, get the first one
                TReleaseBean releaseBean = releases.get(0);
                return releaseBean.getObjectID();
            }
        }
        return null;

    }

    /**
     * Get default issue type for project
     * @param projectBean
     * @param originatorID
     * @return
     */
    static Integer getDefaultIssueType(TProjectBean projectBean, Integer originatorID) {
        Integer projectID = projectBean.getObjectID();
        String strDefaultIssueType = PropertiesHelper.getProperty(projectBean.getMoreProperties(),
                TProjectBean.MOREPPROPS.DEFAULT_ISSUETYPE);
        Integer defaultIssueType = null;
        if (strDefaultIssueType != null && !"".equals(strDefaultIssueType)) {
            try {
                defaultIssueType = Integer.valueOf(strDefaultIssueType);
            } catch (Exception e) {
                LOGGER.warn("Converting the default issue type " + strDefaultIssueType + " for project " + projectID
                        + " to integer failed with " + e.getMessage());
                LOGGER.debug(ExceptionUtils.getStackTrace(e));
            }
        }
        return getDefaultIfSpecifiedAndExists(defaultIssueType,
                (List) IssueTypeBL.loadByPersonAndProjectAndRight(originatorID, projectID,
                        new int[] { AccessBeans.AccessFlagIndexes.CREATETASK }),
                true);
    }

    /**
     * Get default priority for project
     * @param projectBean
     * @return
     */
    static Integer getDefaultPriority(TProjectBean projectBean) {
        Integer projectID = projectBean.getObjectID();
        String strDefaultPriority = PropertiesHelper.getProperty(projectBean.getMoreProperties(),
                TProjectBean.MOREPPROPS.DEFAULT_PRIORITY);
        Integer defaultPriority = null;
        if (strDefaultPriority != null && !"".equals(strDefaultPriority)) {
            try {
                defaultPriority = Integer.valueOf(strDefaultPriority);
            } catch (Exception e) {
                LOGGER.warn("Converting the default priority " + strDefaultPriority + " for project " + projectID
                        + " to integer failed with " + e.getMessage());
                LOGGER.debug(ExceptionUtils.getStackTrace(e));
            }
        }
        return getDefaultIfSpecifiedAndExists(defaultPriority, (List) PriorityBL
                .loadAllowedByProjectTypesAndIssueTypes(new Integer[] { projectBean.getProjectType() }, null),
                true);
    }

    /**
     * Get default severity for project
     * @param projectBean
     * @return
     */
    static Integer getDefaultSeverity(TProjectBean projectBean, TFieldConfigBean fieldConfigBean) {
        Integer projectID = projectBean.getObjectID();
        String strDefaultSeverity = PropertiesHelper.getProperty(projectBean.getMoreProperties(),
                TProjectBean.MOREPPROPS.DEFAULT_SEVERITY);
        Integer defaultSeverity = null;
        if (strDefaultSeverity != null && !"".equals(strDefaultSeverity)) {
            try {
                defaultSeverity = Integer.valueOf(strDefaultSeverity);
            } catch (Exception e) {
                LOGGER.warn("Converting the default severity " + strDefaultSeverity + " for project " + projectID
                        + " to integer failed with " + e.getMessage());
                LOGGER.debug(ExceptionUtils.getStackTrace(e));
            }
        }
        boolean fallback = false;
        if (fieldConfigBean != null && fieldConfigBean.isRequiredString()) {
            fallback = true;
        }
        return getDefaultIfSpecifiedAndExists(defaultSeverity, (List) SeverityBL
                .loadAllowedByProjectTypesAndIssueTypes(new Integer[] { projectBean.getProjectType() }, null),
                fallback);
    }

    /**
     * Get the selected prefill value
     * @param projectBean
     * @return
     */
    public static Integer getPrefillValue(TProjectBean projectBean) {
        return PropertiesHelper.getIntegerProperty(projectBean.getMoreProps(), TProjectBean.MOREPPROPS.PREFILL);
    }

    /**
     * Gets the default system list values set for the project  
     * @param fieldID
     * @param projectBean
     * @param originatorID
     * @param issueTypeID
     * @param fieldConfigBean is taken into account only for the optional system lists. 
     * For mandatory system lists we always fall back to the 
     * first entry if the default value is not valid, independently 
     * of the fieldConfigBean's required flag 
     * @return
     */
    public static Integer getDefaultFieldValueForProject(Integer fieldID, TProjectBean projectBean,
            Integer originatorID, Integer issueTypeID, TFieldConfigBean fieldConfigBean) {
        switch (fieldID.intValue()) {
        case SystemFields.ISSUETYPE:
            return getDefaultIssueType(projectBean, originatorID);
        case SystemFields.STATE:
            return ProjectConfigBL.getDefaultIfSpecifiedAndExists(
                    projectBean.getDefaultInitStateID(), (List) StatusBL
                            .getByProjectTypeIssueTypeAssignments(projectBean.getProjectType(), issueTypeID, null),
                    true);
        case SystemFields.MANAGER:
            return ProjectConfigBL.getDefaultIfSpecifiedAndExists(projectBean.getDefaultManagerID(),
                    (List) PersonBL.loadManagersByProjectAndIssueType(projectBean.getObjectID(), issueTypeID),
                    true);
        case SystemFields.RESPONSIBLE:
            return ProjectConfigBL.getDefaultIfSpecifiedAndExists(projectBean.getDefaultOwnerID(),
                    (List) PersonBL.loadResponsiblesByProjectAndIssueType(projectBean.getObjectID(), issueTypeID),
                    true);
        case SystemFields.RELEASENOTICED:
            return getDefaultRelease(projectBean, true, fieldConfigBean);
        case SystemFields.RELEASESCHEDULED:
            return getDefaultRelease(projectBean, false, fieldConfigBean);
        case SystemFields.PRIORITY:
            return getDefaultPriority(projectBean);
        case SystemFields.SEVERITY:
            return getDefaultSeverity(projectBean, fieldConfigBean);
        }
        return null;
    }

    /**
     * Verify whether the default value is valid
     * @param defaultValue
     * @param lableBeanList
     * @param fallBackToFirst
     * @return
     */
    public static Integer getDefaultIfSpecifiedAndExists(Integer defaultValue, List<ILabelBean> lableBeanList,
            boolean fallBackToFirst) {
        if (lableBeanList == null || lableBeanList.isEmpty()) {
            return null;
        }
        if (defaultValue != null) {
            for (ILabelBean labelBean : lableBeanList) {
                if (labelBean.getObjectID().equals(defaultValue)) {
                    return defaultValue;
                }
            }
        }
        if (fallBackToFirst) {
            ILabelBean labelBean = lableBeanList.get(0);
            return labelBean.getObjectID();
        }
        return null;
    }

    private static String getText(String s, Locale locale) {
        return LocalizeUtil.getLocalizedTextFromApplicationResources(s, locale);
    }

    /**
     * This method creates a private workspace.
     * @param personBean Private workspace owner.
     * @param locale 
     */
    public static void createWorkspaceForNewUser(TPersonBean personBean, Locale locale) {
        List<TProjectBean> privateProjects = ProjectBL.getPrivateProject(personBean.getObjectID());
        if (privateProjects == null || privateProjects.isEmpty()) {
            //be sure to add only if not already exists
            TProjectBean projectBean = new TProjectBean();
            ProjectBaseTO projectBaseTO = new ProjectBaseTO();
            List<TSystemStateBean> projectStatusList = SystemStatusBL
                    .getStatusOptions(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, locale);
            if (projectStatusList != null && projectStatusList.size() > 0) {
                projectBaseTO.setProjectStatusID(projectStatusList.get(0).getObjectID());
            }
            List<TProjectTypeBean> projectTypesList = ProjectTypesBL.loadPrivate();
            if (projectTypesList != null && projectTypesList.size() > 0) {
                projectBaseTO.setProjectTypeID(projectTypesList.get(0).getObjectID());
            }
            projectBaseTO.setLabel(LocalizeUtil
                    .getLocalizedTextFromApplicationResources("admin.project.lbl.privateProjectName", locale));
            ProjectDefaultsTO projectDefaultsTO = new ProjectDefaultsTO();
            Integer projectTypeID = projectBean.getProjectType();
            projectDefaultsTO.setDefaultManagerID(personBean.getObjectID());
            projectDefaultsTO.setDefaultResponsibleID(personBean.getObjectID());
            Map<Integer, String> invalidDefaults = setSystemLists(projectBean, projectDefaultsTO, projectTypeID,
                    locale, false);
            if (invalidDefaults.isEmpty()) {
                saveProjectDetail(null, true, false, true, projectBaseTO, null, projectDefaultsTO, false, false,
                        personBean.getObjectID(), locale);
            }
        }
    }

    public static boolean changeTemplateStateTo(TProjectBean templateBean, Integer stateFlagToChange,
            Locale locale) {
        List<TSystemStateBean> projectStatusList = SystemStatusBL
                .getStatusOptions(TSystemStateBean.ENTITYFLAGS.PROJECTSTATE, locale);
        TSystemStateBean properState = null;
        for (TSystemStateBean aStateBean : projectStatusList) {
            if (aStateBean.getStateflag().intValue() == stateFlagToChange.intValue()) {
                properState = aStateBean;
            }
        }
        if (properState != null) {
            templateBean.setStatus(properState.getObjectID());
            ProjectBL.saveSimple(templateBean);
            return true;
        }
        return false;
    }
}