com.aurel.track.accessControl.AccessBeans.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.accessControl.AccessBeans.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.accessControl;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.accessControl.AccessControlBL;
import com.aurel.track.admin.customize.objectStatus.SystemStatusBL;
import com.aurel.track.admin.customize.role.FieldsRestrictionsToRoleBL;
import com.aurel.track.admin.customize.role.RoleBL;
import com.aurel.track.admin.project.ProjectBL;
import com.aurel.track.admin.user.group.GroupConfigBL;
import com.aurel.track.admin.user.group.GroupMemberBL;
import com.aurel.track.admin.user.person.PersonBL;
import com.aurel.track.beans.ILabelBean;
import com.aurel.track.beans.TAccessControlListBean;
import com.aurel.track.beans.TAttributeValueBean;
import com.aurel.track.beans.TBudgetBean;
import com.aurel.track.beans.TCostBean;
import com.aurel.track.beans.TFieldConfigBean;
import com.aurel.track.beans.TListTypeBean;
import com.aurel.track.beans.TNotifyBean;
import com.aurel.track.beans.TPersonBean;
import com.aurel.track.beans.TProjectBean;
import com.aurel.track.beans.TRoleBean;
import com.aurel.track.beans.TRoleFieldBean;
import com.aurel.track.beans.TRoleListTypeBean;
import com.aurel.track.beans.TStateBean;
import com.aurel.track.beans.TSystemStateBean;
import com.aurel.track.beans.TWorkItemBean;
import com.aurel.track.fieldType.constants.SystemFields;
import com.aurel.track.fieldType.runtime.base.IFieldTypeRT;
import com.aurel.track.fieldType.runtime.base.LookupContainer;
import com.aurel.track.fieldType.runtime.base.WorkItemContext;
import com.aurel.track.fieldType.runtime.bl.AttributeValueBL;
import com.aurel.track.fieldType.runtime.bl.FieldRuntimeBL;
import com.aurel.track.fieldType.runtime.custom.picker.UserPickerRT;
import com.aurel.track.fieldType.runtime.helpers.MergeUtil;
import com.aurel.track.fieldType.types.FieldTypeManager;
import com.aurel.track.fieldType.types.system.text.SystemTextBoxDate.HIERARCHICAL_BEHAVIOR_OPTIONS;
import com.aurel.track.item.ItemBL;
import com.aurel.track.item.consInf.ConsInfBL;
import com.aurel.track.item.consInf.RaciRole;
import com.aurel.track.item.workflow.execute.StatusWorkflow;
import com.aurel.track.prop.ApplicationBean;
import com.aurel.track.report.execute.ReportBean;
import com.aurel.track.report.execute.ReportBeanWithHistory;
import com.aurel.track.util.GeneralUtils;
import com.aurel.track.util.PropertiesHelper;
import com.workingdogs.village.DataSetException;
import com.workingdogs.village.Record;

/**
 * This class provides an AccessBeans ArrayList mainly for managing user access
 * rights and access to specific issues. It loads all role/project combinations
 * for one person from table TACL in the database and stores the access rights
 * in a hashtable for each project the key being the project id, and the value
 * being an another map with access flag values
 *
 * Furthermore, this class checks to what list types a user has access in a
 * specific project. It creates a hash set containing entries where projectid
 * and list type id are concatenated strings.
 *
 */
public class AccessBeans implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final String theProject = "The project ";
    private static final String addItem = "Add item ";
    private static final String noRole = "No role found for person ";
    private static final String remaining = " remaining to verify";

    public static final char ONVALUE = '1';
    public static final char OFFVALUE = '0';

    private static final Logger LOGGER = LogManager.getLogger(AccessBeans.class);

    private AccessBeans() {
    }

    /**
     * Gets the array with the person and the groups the person is member of
     *
     * @param personID
     * @return
     */
    public static List<Integer> getMeAndMyGroups(Integer personID) {
        List<Integer> personIDs = new LinkedList<Integer>();
        if (personID != null) {
            personIDs.add(personID);
            List<TPersonBean> groupBeans = PersonBL.loadGroupsForPerson(personID);
            if (groupBeans != null && !groupBeans.isEmpty()) {
                LOGGER.debug("Number of groups found for person " + personID + ": " + groupBeans.size());
                personIDs.addAll(GeneralUtils.createIntegerListFromBeanList(groupBeans));
            }
        }
        return personIDs;
    }

    /**
     * Gets a list with personID and the persons substituted by personID
     *
     * @param personID
     * @return
     */
    public static List<Integer> getMeAndSubstituted(TPersonBean personBean) {
        List<Integer> personIDs = new LinkedList<Integer>();
        if (personBean != null) {
            Integer personID = personBean.getObjectID();
            personIDs.add(personID);
            List<Integer> substitutePersons = personBean.getSubstitutedPersons();
            if (substitutePersons == null) {
                LOGGER.debug("Get the substitute persons from the database");
                substitutePersons = PersonBL.loadSubstitutedPersons(personID);
                if (substitutePersons != null) {
                    // set even if it is of length 0 to avoid loading from DB
                    // next time
                    personBean.setSubstitutedPersons(substitutePersons);
                    LOGGER.debug("Number of substitute persons found for person " + personID + ": "
                            + substitutePersons.size());
                }
            }
            if (substitutePersons != null) {
                personIDs.addAll(substitutePersons);
            }
        }
        return personIDs;
    }

    /**
     * Gets a list with personID and the persons substituted by personID
     *
     * @param personID
     * @return
     */
    public static List<Integer> getMeAndSubstituted(Integer personID) {
        TPersonBean personBean = LookupContainer.getPersonBean(personID);
        return getMeAndSubstituted(personBean);
    }

    /**
     * Gets a list with personID, the persons substituted by personID and all
     * groups they belong to
     *
     * @param personID
     * @return
     */
    public static List<Integer> getMeAndSubstitutedAndGroups(Integer personID) {
        List<Integer> personIDs = getMeAndSubstituted(personID);
        List<TPersonBean> groupsForPersons = PersonBL.loadGroupsForPersons(personIDs);
        if (groupsForPersons != null && !groupsForPersons.isEmpty()) {
            LOGGER.debug("Number of groups found for person (and substitutes)" + personID + ": "
                    + groupsForPersons.size());
            personIDs.addAll(GeneralUtils.createIntegerListFromBeanList(groupsForPersons));
        }
        return personIDs;
    }

    /**
     * Creates an array of like strings for extendedAccessKeys
     *
     * @param arrFlagIndexes
     *            value from AccessFlagMigrationIndexes
     * @return
     */
    public static String[] likeFilterString(int[] arrFlagIndexes) {
        String[] filterStrings = new String[arrFlagIndexes.length];
        StringBuilder flags;
        for (int i = 0; i < arrFlagIndexes.length; i++) {
            flags = new StringBuilder();
            for (int j = 0; j < arrFlagIndexes[i]; j++) {
                flags.append('?');
            }
            flags.append(ONVALUE);
            flags.append('*');
            filterStrings[i] = flags.toString();
        }
        return filterStrings;
    }

    /**
     * Creates an array of like strings for extendedAccessKeys
     *
     * @return
     */
    public static String anyRightFilterString() {
        return "*" + ONVALUE + "*";
    }

    /**
     * Whether is allowed to create new issue in a project for a listType In
     * create phase no raci roles are involved only explicit roles count
     *
     * @param personID
     * @param projectID
     * @param listTypeID
     * @return
     */
    public static boolean isAllowedToCreate(Integer personID, Integer projectID, Integer listTypeID) {
        return hasPersonRightInProjectForIssueType(personID, projectID, listTypeID, AccessFlagIndexes.CREATETASK,
                true, true);
    }

    /**
     * Checks whether the person logged on has read access to the item
     *
     * @param workItemBean
     * @param personID
     * @return
     */
    public static boolean isAllowedToRead(TWorkItemBean workItemBean, Integer personID) {
        if (personID == null) {
            LOGGER.warn("Not allowed to read, personID not specified");
            return false;
        }
        if (workItemBean == null) {
            LOGGER.warn("Not allowed to read, workItemBean not specified");
            return false;
        }
        // whether the access level permits the read (not private for another
        // person)
        if (workItemBean.isAccessLevelFlag() && !workItemBean.getOriginatorID().equals(personID)) {
            LOGGER.debug("Not allowed to read private workItem " + workItemBean.getObjectID());
            return false;
        }

        Integer project = workItemBean.getProjectID();
        if (project == null) {
            LOGGER.warn("Not allowed to read, no project for workItem");
            return false;
        } else {
            if (SystemStatusBL.getStatusFlag(project,
                    TSystemStateBean.ENTITYFLAGS.PROJECTSTATE) == TSystemStateBean.STATEFLAGS.CLOSED) {
                LOGGER.debug("Not allowed to read the workItem " + workItemBean.getObjectID()
                        + " from a closed project");
                return false;
            }
        }
        if (workItemBean.isArchivedOrDeleted()) {
            TPersonBean personBean = LookupContainer.getPersonBean(personID);
            if (!PersonBL.isProjectAdmin(personID, project) && (personBean == null || !personBean.isSys())) {
                LOGGER.debug("Not allowed to read the archived/deleted workItem " + workItemBean.getObjectID());
                return false;
            }
        }
        return isAllowed(workItemBean, personID, AccessFlagIndexes.READANYTASK, null);
    }

    /**
     * Checks whether the person logged on has modify access to the item
     * (directly or through raci role)
     *
     * @param workItemBean
     * @return
     */
    public static boolean isAllowedToChange(TWorkItemBean workItemBean, Integer personID) {
        if (personID == null) {
            LOGGER.warn("Not allowed to change, personID not specified");
            return false;
        }
        if (workItemBean == null) {
            LOGGER.warn("Not allowed to change, workItemBean not specified");
            return false;
        }
        // whether the access level forbids to read and implicitly to write
        // (private for another person)
        if (workItemBean.isAccessLevelFlag() && !workItemBean.getOriginatorID().equals(personID)) {
            LOGGER.debug("Not allowed to change private workItem " + workItemBean.getObjectID());
            return false;
        }
        Integer project = workItemBean.getProjectID();
        if (project == null) {
            LOGGER.warn("Not allowed to modify, no project for workItem");
            return false;
        } else {
            int projectStatusFlag = SystemStatusBL.getStatusFlag(project,
                    TSystemStateBean.ENTITYFLAGS.PROJECTSTATE);
            if (projectStatusFlag != TSystemStateBean.STATEFLAGS.ACTIVE) {
                LOGGER.debug("Not allowed to change the workItem " + workItemBean.getObjectID()
                        + " from a closed or inactive project");
                return false;
            }
        }
        if (workItemBean.isArchivedOrDeleted()) {
            TPersonBean personBean = LookupContainer.getPersonBean(personID);
            if (!PersonBL.isProjectAdmin(personID, project) && (personBean == null || !personBean.isSys())) {
                LOGGER.debug("Not allowed to change the archived/deleted workItem " + workItemBean.getObjectID());
                return false;
            }
        }
        Integer statusID = workItemBean.getStateID();
        TStateBean stateBean = LookupContainer.getStatusBean(statusID);
        if (stateBean != null && stateBean.getStateflag() != null
                && stateBean.getStateflag().intValue() == TStateBean.STATEFLAGS.DISABLED) {
            List<TStateBean> stateBeanList = StatusWorkflow.loadStatesTo(project, workItemBean.getListTypeID(),
                    statusID, personID, workItemBean, null);
            if (stateBeanList.size() == 1) {
                LOGGER.debug("According to the workflow changing the disabled workItem is not allowed "
                        + workItemBean.getObjectID());
                return false;
            }
        }
        return isAllowed(workItemBean, personID, AccessFlagIndexes.MODIFYANYTASK, RaciRole.CONSULTANT);
    }

    /**
     * Gets the "on behalf of" persons from the workItem's custom attributes
     *
     * @param workItemBean
     */
    private static Set<Integer> getOnBehalfOfPersons(TWorkItemBean workItemBean) {
        Set<Integer> onBehalfOfPersons = null;
        Map<String, Object> customAttributes = workItemBean.getCustomAttributeValues();
        if (customAttributes == null) {
            // if the custom attributes are not yet loaded load only the pickers
            customAttributes = AttributeValueBL.loadWorkItemCustomUserPickerAttributes(workItemBean);
        }
        if (customAttributes != null && !customAttributes.isEmpty()) {
            for (String mergeKey : customAttributes.keySet()) {
                Object fieldValue = customAttributes.get(mergeKey);
                if (fieldValue != null) {
                    Integer[] keyComponents = MergeUtil
                            .getParts(mergeKey.substring(TWorkItemBean.PREFIX_LETTER.length()));
                    Integer fieldID = keyComponents[0];
                    IFieldTypeRT fieldTypeRT = FieldTypeManager.getFieldTypeRT(fieldID);
                    if (fieldTypeRT.isUserPicker()) {
                        UserPickerRT userPickerRT = (UserPickerRT) fieldTypeRT;
                        if (userPickerRT.inheritsOriginatorRole()) {
                            if (onBehalfOfPersons == null) {
                                onBehalfOfPersons = new HashSet<Integer>();
                            }
                            Object[] fieldValues = null;
                            try {
                                fieldValues = (Object[]) fieldValue;
                                if (fieldValues != null && fieldValues.length > 0) {
                                    onBehalfOfPersons.add((Integer) fieldValues[0]);
                                }
                            } catch (Exception e) {
                                LOGGER.info(e.getMessage());
                                LOGGER.debug(ExceptionUtils.getStackTrace(e));
                            }
                        }
                    }
                }
            }
        }
        return onBehalfOfPersons;
    }

    /**
     * Returns all direct on behalf persons but load all on behalf persons
     * (direct an through group) into onBehalfPersonsAll
     *
     * @param onBehalfOfPersonsOrGroups
     * @param onBehalfPersonsAll
     * @return
     */
    private static Set<Integer> getOnBehalfPersonsDirect(Set<Integer> onBehalfOfPersonsOrGroups,
            Set<Integer> onBehalfPersonsAll) {
        Set<Integer> onBehalfPersonsDirect = new HashSet<Integer>();
        Set<Integer> onBehalfPersonsThroughGroup = new HashSet<Integer>();
        List<Integer> onBehalfGroups = new LinkedList<Integer>();
        if (onBehalfOfPersonsOrGroups != null && !onBehalfOfPersonsOrGroups.isEmpty()) {
            List<TPersonBean> onBehalfPersonBeans = PersonBL
                    .loadByKeys(GeneralUtils.createIntegerListFromCollection(onBehalfOfPersonsOrGroups));
            if (onBehalfPersonBeans != null) {
                for (TPersonBean personBean : onBehalfPersonBeans) {
                    Integer personID = personBean.getObjectID();
                    if (personBean.isGroup()) {
                        onBehalfGroups.add(personID);
                        LOGGER.trace("On behalf group " + personBean.getName() + " (" + personBean.getObjectID()
                                + ")  found");
                    } else {
                        onBehalfPersonsDirect.add(personID);
                        LOGGER.trace("On behalf person " + personBean.getName() + " (" + personBean.getObjectID()
                                + ")  found");
                    }
                }
                if (!onBehalfGroups.isEmpty()) {
                    List<TPersonBean> onBehalfPersonsThroughGroupBeans = PersonBL
                            .loadPersonsForGroups(onBehalfGroups);
                    if (onBehalfPersonsThroughGroupBeans != null && !onBehalfPersonsThroughGroupBeans.isEmpty()) {
                        onBehalfPersonsThroughGroup = GeneralUtils
                                .createIntegerSetFromBeanList(onBehalfPersonsThroughGroupBeans);
                    }
                }
            }
        }
        onBehalfPersonsAll.addAll(onBehalfPersonsDirect);
        onBehalfPersonsAll.addAll(onBehalfPersonsThroughGroup);
        return onBehalfPersonsDirect;
    }

    /**
     * Whether the person (directly or as substitute) has a right in the item
     *
     * @param workItemBean
     * @param personID
     * @param permissionFlag
     * @param watcherRole
     * @return
     */
    private static boolean isAllowed(TWorkItemBean workItemBean, Integer personID, int permissionFlag,
            String watcherRole) {
        if (personID == null) {
            LOGGER.debug("Not allowed, personID not specified");
            return false;
        }
        List<Integer> meAndSubstitutes = getMeAndSubstituted(personID);
        Integer workItemID = workItemBean.getObjectID();
        Integer originatorID = workItemBean.getOriginatorID();
        Integer responsibleID = workItemBean.getResponsibleID();
        Integer managerID = workItemBean.getOwnerID();
        Integer project = workItemBean.getProjectID();
        Integer listType = workItemBean.getListTypeID();
        boolean isOriginator = meAndSubstitutes.contains(originatorID);
        boolean isReponsible = meAndSubstitutes.contains(responsibleID);
        boolean isManager = meAndSubstitutes.contains(managerID);
        if (isOriginator || isReponsible || isManager) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID + " through raci role");
            }
            return true;
        }
        if (GroupMemberBL.isAnyPersonMemberInGroup(meAndSubstitutes, responsibleID)) {
            // responsible through group
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID
                        + " through person member in group");
            }
            return true;
        }
        if (ConsInfBL.hasRaciRole(workItemBean.getObjectID(), meAndSubstitutes, watcherRole)) {
            // consultant/informant
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID + " through watchers");
            }
            return true;
        }
        // try explicit modify role
        if (hasPersonRightInProjectForIssueType(personID, project, listType, permissionFlag, true, true)) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID
                        + " through explicit project role");
            }
            return true;
        }
        Set<Integer> onBehalfOfPersonsOrGroups = getOnBehalfOfPersons(workItemBean);
        Set<Integer> onBehalfPersonsAll = new HashSet<Integer>();
        Set<Integer> onBehalfPersonsDirect = getOnBehalfPersonsDirect(onBehalfOfPersonsOrGroups,
                onBehalfPersonsAll);
        if (hasCommon(meAndSubstitutes, onBehalfPersonsAll)) {
            LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID
                    + " through on behalf of user picker");
            return true;
        }
        Map<Integer, List<Integer>> reciprocGroupsMap = getRaciprocGroupsMap(meAndSubstitutes);
        Set<Integer> originatorPersons = new HashSet<Integer>();
        originatorPersons.add(originatorID);
        if (onBehalfPersonsDirect != null) {
            originatorPersons.addAll(onBehalfPersonsDirect);
        }
        if (isReciprocByGroup(reciprocGroupsMap.get(SystemFields.INTEGER_ORIGINATOR), originatorPersons)) {
            LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID
                    + " through group reciproc originator flag");
            return true;
        }
        if (isReciprocByGroup(reciprocGroupsMap.get(SystemFields.INTEGER_MANAGER), managerID)) {
            LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID
                    + " through group reciproc manager flag");
            return true;
        }
        if (isReciprocByGroup(reciprocGroupsMap.get(SystemFields.INTEGER_RESPONSIBLE), responsibleID)) {
            LOGGER.debug("Permission " + permissionFlag + " for workItem " + workItemID
                    + " through group reciproc responsible flag");
            return true;
        }
        return false;
    }

    /**
     * Checks whether the person logged on may close the item
     *
     * @param workItemBean
     * @param personID
     * @return
     */
    public static boolean isAllowedToClose(TWorkItemBean workItemBean, Integer personID) {
        if (personID == null) {
            LOGGER.debug("Not allowed to close, personID not specified");
            return false;
        }
        List<Integer> meAndSubstitutes = getMeAndSubstituted(personID);
        if (workItemBean == null) {
            LOGGER.debug("Not allowed to close, workItemBean not specified");
            return false;
        }
        Integer project = workItemBean.getProjectID();
        if (project == null) {
            LOGGER.debug("Not allowed to close, no project for workItem");
            return false;
        }
        Integer workItemID = workItemBean.getObjectID();
        Integer listType = workItemBean.getListTypeID();
        Integer originatorID = workItemBean.getOriginatorID();
        Integer reponsibleID = workItemBean.getResponsibleID();
        Integer managerID = workItemBean.getOwnerID();
        boolean isReponsible = meAndSubstitutes.contains(reponsibleID);
        boolean isOriginator = meAndSubstitutes.contains(originatorID);
        boolean isManager = meAndSubstitutes.contains(managerID);
        boolean closeIfOrigMan = true;
        boolean closeIfResp = true;
        // explicit "close any" right
        if (hasPersonRightInProjectForIssueType(personID, project, listType, AccessFlagIndexes.CLOSEANYTASK, true,
                true)) {
            LOGGER.debug("'Close any' perimission for workItem " + workItemID);
            return true;
        }
        // originator or manager and explicit "close if man. or orig." right
        if (isOriginator || isManager) {
            if (hasPersonRightInProjectForIssueType(personID, project, listType,
                    AccessFlagIndexes.CLOSETASKIFMANAGERORORIGINATOR, true, true)) {
                LOGGER.debug("'Close if manager or originator' perimission for workItem " + workItemID);
                return true;
            } else {
                closeIfOrigMan = false;
            }
        }
        // responsible (direct or through group) and explicit "close if resp."
        // right
        if (isReponsible || GroupMemberBL.isPersonMemberInGroup(personID, reponsibleID)) {
            if (hasPersonRightInProjectForIssueType(personID, project, listType,
                    AccessFlagIndexes.CLOSETASKIFRESPONSIBLE, true, true)) {
                LOGGER.debug("'Close if responsible' perimission for workItem " + workItemID);
                return true;
            } else {
                closeIfResp = false;
            }
        }
        // on behalf
        Set<Integer> onBehalfPersonsDirect = null;
        if (!isOriginator && closeIfOrigMan) {
            Set<Integer> onBehalfOfPersonsOrGroups = getOnBehalfOfPersons(workItemBean);
            Set<Integer> onBehalfPersonsAll = new HashSet<Integer>();
            onBehalfPersonsDirect = getOnBehalfPersonsDirect(onBehalfOfPersonsOrGroups, onBehalfPersonsAll);
            if (onBehalfPersonsAll != null && !onBehalfPersonsAll.isEmpty() && onBehalfPersonsAll.contains(personID)
                    && closeIfOrigMan) {
                if (hasPersonRightInProjectForIssueType(personID, project, listType,
                        AccessFlagIndexes.CLOSETASKIFMANAGERORORIGINATOR, true, true)) {
                    LOGGER.debug("'Close if manager or originator' perimission for workItem " + workItemID
                            + " by on behalf of " + personID);
                    return true;
                } else {
                    closeIfOrigMan = false;
                }
            }
        }
        Map<Integer, List<Integer>> reciprocGroupsMap = getRaciprocGroupsMap(meAndSubstitutes);
        if (!isOriginator && closeIfOrigMan) {
            Set<Integer> originatorIDs = new HashSet<Integer>();
            originatorIDs.add(originatorID);
            if (onBehalfPersonsDirect != null) {
                originatorIDs.addAll(onBehalfPersonsDirect);
            }
            isOriginator = isReciprocByGroup(reciprocGroupsMap.get(SystemFields.INTEGER_ORIGINATOR), originatorIDs);
            if (isOriginator && hasPersonRightInProjectForIssueType(personID, project, listType,
                    AccessFlagIndexes.CLOSETASKIFMANAGERORORIGINATOR, true, true)) {
                LOGGER.debug("'Close if manager or originator' perimission for workItem " + workItemID
                        + " originator by reciproc group");
                return true;
            }
        }
        if (!isManager && closeIfOrigMan) {
            isManager = isReciprocByGroup(reciprocGroupsMap.get(SystemFields.INTEGER_MANAGER), managerID);
            if (isManager && hasPersonRightInProjectForIssueType(personID, project, listType,
                    AccessFlagIndexes.CLOSETASKIFMANAGERORORIGINATOR, true, true)) {
                LOGGER.debug("'Close if manager or originator' perimission for workItem " + workItemID
                        + " manager by reciproc group");
                return true;
            }
        }
        if (!isReponsible && closeIfResp) {
            isReponsible = isReciprocByGroup(reciprocGroupsMap.get(SystemFields.INTEGER_RESPONSIBLE), reponsibleID);
            if (isReponsible && hasPersonRightInProjectForIssueType(personID, project, listType,
                    AccessFlagIndexes.CLOSETASKIFRESPONSIBLE, true, true)) {
                LOGGER.debug("'Close if responsible' perimission for workItem " + workItemID
                        + " responsible by reciproc group");
                return true;
            }
        }
        return false;
    }

    /**
     * Gets the groups the person is member of having the corresponding right
     * flag set
     *
     * @param personID
     */
    public static Map<Integer, List<Integer>> getRaciprocGroupsMap(List<Integer> personIDs) {
        Map<Integer, List<Integer>> reciprocGroupsMap = new HashMap<Integer, List<Integer>>();
        List<TPersonBean> groupsForPerson = PersonBL.loadGroupsForPersons(personIDs);
        if (groupsForPerson != null) {
            for (TPersonBean groupBean : groupsForPerson) {
                Integer groupID = groupBean.getObjectID();
                addToMap(groupID, reciprocGroupsMap, true, ALL_PERSONGROUPS);
                boolean isOrginator = PropertiesHelper.getBooleanProperty(groupBean.getPreferences(),
                        GroupConfigBL.GROUP_FLAGS.ORIGINATOR);
                addToMap(groupID, reciprocGroupsMap, isOrginator, SystemFields.INTEGER_ORIGINATOR);
                boolean isManager = PropertiesHelper.getBooleanProperty(groupBean.getPreferences(),
                        GroupConfigBL.GROUP_FLAGS.MANAGER);
                addToMap(groupID, reciprocGroupsMap, isManager, SystemFields.INTEGER_MANAGER);
                boolean isResponsible = PropertiesHelper.getBooleanProperty(groupBean.getPreferences(),
                        GroupConfigBL.GROUP_FLAGS.RESPONSIBLE);
                addToMap(groupID, reciprocGroupsMap, isResponsible, SystemFields.INTEGER_RESPONSIBLE);
            }
        }
        return reciprocGroupsMap;
    }

    public static final Integer ALL_PERSONGROUPS = Integer.valueOf(0);

    /**
     * Add group to raci map
     *
     * @param groupID
     * @param reciprocGroupsMap
     * @param checked
     * @param raciField
     */
    private static void addToMap(Integer groupID, Map<Integer, List<Integer>> reciprocGroupsMap, boolean checked,
            Integer raciField) {
        if (checked) {
            List<Integer> raciList = reciprocGroupsMap.get(raciField);
            if (raciList == null) {
                raciList = new LinkedList<Integer>();
                reciprocGroupsMap.put(raciField, raciList);
            }
            raciList.add(groupID);
        }
    }

    /**
     * Whether any person from raciPersons is member of any group from
     * reciprocGroups
     *
     * @param reciprocGroups
     * @param raciPersons
     * @return
     */
    private static boolean isReciprocByGroup(List<Integer> reciprocGroups, Set<Integer> raciPersons) {
        if (reciprocGroups != null && !reciprocGroups.isEmpty() && raciPersons != null && !raciPersons.isEmpty()) {
            return GroupMemberBL.isAnyPersonMemberInAnyGroup(
                    GeneralUtils.createIntegerListFromCollection(raciPersons), reciprocGroups);
        } else {
            return false;
        }
    }

    /**
     * Whether any person from raciPersons is member of any group from
     * reciprocGroups
     *
     * @param reciprocGroups
     * @param raciPerson
     * @return
     */
    private static boolean isReciprocByGroup(List<Integer> reciprocGroups, Integer raciPerson) {
        if (reciprocGroups != null && !reciprocGroups.isEmpty() && raciPerson != null) {
            return GroupMemberBL.isPersonMemberInAnyGroup(raciPerson, reciprocGroups);
        } else {
            return false;
        }
    }

    /**
     * Gets the restrictions for budget, plan and expenses
     *
     * @param personID
     * @param projectID
     * @param listTypeID
     * @return
     */
    public static Map<Integer, Integer> getBudgetPlanExpenseRestrictions(Integer personID, Integer projectID,
            Integer listTypeID) {
        List<Integer> fields = new LinkedList<Integer>();
        fields.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.PLAN);
        fields.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.BUDGET);
        fields.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.MY_EXPENSES);
        fields.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES);
        return getFieldRestrictions(personID, projectID, listTypeID, fields, true);
    }

    /**
     * Whether the user has any right in the accounting tab
     *
     * @param personID
     * @param projectID
     * @param listTypeID
     * @return
     */
    public static boolean showAccountingTab(Integer personID, Integer projectID, Integer listTypeID) {
        Map<Integer, Integer> fieldRestrictions = getBudgetPlanExpenseRestrictions(personID, projectID, listTypeID);
        if (fieldRestrictions == null) {
            return true;
        }
        Integer planRestrictions = fieldRestrictions.get(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.PLAN);
        if (planRestrictions == null || planRestrictions.intValue() != TRoleFieldBean.ACCESSFLAG.NOACCESS) {
            // right to read or modify plan
            return true;
        }
        boolean budgetActive = ApplicationBean.getInstance().getBudgetActive();
        Integer budgetRestrictions = fieldRestrictions.get(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.BUDGET);
        if (budgetActive && (budgetRestrictions == null
                || budgetRestrictions.intValue() != TRoleFieldBean.ACCESSFLAG.NOACCESS)) {
            // right to read or modify budget
            return true;
        }
        Integer myExpensesRestrictions = fieldRestrictions
                .get(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.MY_EXPENSES);
        if (myExpensesRestrictions == null) {
            // right to add own expense
            return true;
        }
        Integer totalExpenseRestrictions = fieldRestrictions
                .get(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES);
        if (totalExpenseRestrictions == null
                || totalExpenseRestrictions.intValue() != TRoleFieldBean.ACCESSFLAG.NOACCESS) {
            // right to see other person's expenses
            return true;
        }
        return false;
    }

    /**
     * Whether the user can see all expenses It is supposed that this code is
     * called only if the read access to the issue is already assured
     *
     * @param personID
     * @param projectID
     * @param listTypeID
     * @return
     */
    public static boolean viewAllExpenses(Integer personID, Integer projectID, Integer listTypeID) {
        List<Integer> fields = new LinkedList<Integer>();
        fields.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES);
        Map<Integer, Integer> fieldRestrictions = getFieldRestrictions(personID, projectID, listTypeID, fields,
                false);
        return fieldRestrictions == null
                || !fieldRestrictions.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES);
    }

    /**
     * Whether the watchers tab is allowed It is supposed that this code is
     * called only if the read access to the issue is already assured
     *
     * @param personID
     * @param projectID
     * @param issueTypeID
     * @return
     */
    public static boolean watchersTabAllowed(Integer personID, Integer projectID, Integer issueTypeID) {
        List<Integer> fields = new LinkedList<Integer>();
        fields.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.WATCHERS);
        Map<Integer, Integer> fieldRestrictions = getFieldRestrictions(personID, projectID, issueTypeID, fields,
                false);
        return fieldRestrictions == null
                || !fieldRestrictions.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.WATCHERS);
    }

    /****************************************** Person set with right in projects ******************************************/

    /**
     * Gets the status IDs assigned to the projectType for an issueType
     *
     * @param projectID
     * @param rights
     * @return
     */
    public static Set<Integer> getPersonSetByProjectRights(Integer projectID, int[] rights) {
        Integer[] projectIDs = GeneralUtils
                .createIntegerArrFromCollection(ProjectBL.getAncestorProjects(projectID));
        return getPersonSetByProjectsRights(projectIDs, rights);
    }

    /**
     * Gets the status IDs assigned to the projectType for an issueType
     *
     * @param projects
     * @param rights
     * @return
     */
    public static Set<Integer> getPersonSetByProjectsRights(Integer[] projects, int[] rights) {
        List<TAccessControlListBean> accessControlListBeans = AccessControlBL.loadByProjectsAndRights(projects,
                rights);
        return getPersonsFromAcList(accessControlListBeans);
    }

    /**
     * Load a list of AccessControlListBeans by projects, roles and listType
     *
     * @param projectID
     * @param roles
     * @param listType
     *            if null do not filter by
     * @return
     */
    public static Set<Integer> loadByProjectsRolesListType(Integer projectID, Object[] roles, Integer listType) {
        List<TAccessControlListBean> accessControlListBeans = AccessControlBL
                .loadByProjectsRolesListType(ProjectBL.getAncestorProjects(projectID), roles, listType);
        return getPersonsFromAcList(accessControlListBeans);
    }

    /**
     * Returns a set of personIDs which have one of the specified rights in all
     * projects
     *
     * @param projects
     * @param arrRights
     *            an array of rights, null means any right
     * @return
     */
    public static Set<Integer> getPersonSetWithRightInAllOfTheProjects(Integer[] projects, int[] arrRights) {
        Set<Integer> personIDs = new HashSet<Integer>();
        if (projects == null || projects.length == 0) {
            return personIDs;
        }
        // get the results for first project
        personIDs = getPersonsFromAcList(AccessControlBL.loadByProjectsAndRights(
                GeneralUtils.createIntegerArrFromCollection(ProjectBL.getAncestorProjects(projects[0])),
                arrRights));
        if (personIDs == null || personIDs.isEmpty()) {
            return personIDs;
        }
        // verify whether all the persons found for the first project appear
        // also in the next projects
        // if not remove it from the result
        for (int i = 1; i < projects.length; i++) {
            Set<Integer> personIDsForProject = getPersonsFromAcList(AccessControlBL.loadByProjectsAndRights(
                    GeneralUtils.createIntegerArrFromCollection(ProjectBL.getAncestorProjects(projects[i])),
                    arrRights));
            if (personIDsForProject == null || personIDsForProject.isEmpty()) {
                return new HashSet<Integer>();
            }
            personIDs.retainAll(personIDsForProject);
        }
        return personIDs;
    }

    /**
     * Gets the distinct roleIDs from a list of TAccessControlListBean beans
     *
     * @param acList
     * @return
     */
    static Set<Integer> getPersonsFromAcList(List<TAccessControlListBean> acList) {
        Set<Integer> personSet = new HashSet<Integer>();
        if (acList != null) {
            for (TAccessControlListBean accessControlListBean : acList) {
                personSet.add(accessControlListBean.getPersonID());
            }
        }
        return personSet;
    }

    /**
     * Gather the explicitly restricted fields (by project roles) and the bottom
     * up date change restrictions
     * @param workItemContext
     * @return
     */
    public static Map<Integer, Integer> getRestrictedFieldsAndBottomUpDates(WorkItemContext workItemContext) {
        TWorkItemBean workItemBean = workItemContext.getWorkItemBean();
        Set<Integer> presentFieldIDs = workItemContext.getPresentFieldIDs();
        // explicit role descriptions
        Map<Integer, Integer> fieldRestrictions = getFieldRestrictions(workItemContext.getPerson(),
                workItemBean.getProjectID(), workItemBean.getListTypeID(), true);
        Map<Integer, TFieldConfigBean> fieldConfigs = workItemContext.getFieldConfigs();
        Map<String, Object> fieldSettings = workItemContext.getFieldSettings();
        // make the dates of the parent workItems read only if not yet
        // restricted and set as compute bottom up
        if (workItemBean.getObjectID() != null) {
            Set<Integer> possibleBottomUpFields = FieldRuntimeBL.getPossibleBottomUpFields();
            Set<Integer> restrictedBottomUpDates = new HashSet<Integer>();
            for (Integer fieldID : possibleBottomUpFields) {
                boolean presentNotRestrictedField = presentFieldIDs.contains(fieldID)
                        && !fieldRestrictions.containsKey(fieldID);
                if (presentNotRestrictedField) {
                    IFieldTypeRT fieldTypeRT = FieldTypeManager.getFieldTypeRT(fieldID);
                    if (fieldConfigs != null) {
                        TFieldConfigBean fieldConfigBean = fieldConfigs.get(fieldID);
                        if (fieldTypeRT.getHierarchicalBehavior(fieldID, fieldConfigBean,
                                fieldSettings.get(MergeUtil.mergeKey(fieldID,
                                        null))) == HIERARCHICAL_BEHAVIOR_OPTIONS.COMPUTE_BOTTOM_UP) {
                            restrictedBottomUpDates.add(fieldID);
                            if (SystemFields.INTEGER_STARTDATE.equals(fieldID)
                                    || SystemFields.INTEGER_ENDDATE.equals(fieldID)) {
                                restrictedBottomUpDates.add(SystemFields.INTEGER_DURATION);
                            } else {
                                restrictedBottomUpDates.add(SystemFields.INTEGER_TOP_DOWN_DURATION);
                            }
                        }
                    }
                }
            }
            if (restrictedBottomUpDates != null && !restrictedBottomUpDates.isEmpty()) {
                if (ItemBL.hasChildren(workItemBean.getObjectID())) {
                    for (Integer fieldID : restrictedBottomUpDates) {
                        fieldRestrictions.put(fieldID, TRoleFieldBean.ACCESSFLAG.READ_ONLY);
                    }
                }
            }
        }
        return fieldRestrictions;
    }

    /**
     * Returns the fields restrictions for a person on a certain project and
     * issueType By convention a field will be returned only if it is restricted
     * either no read (and consequently no modify) or no modify right
     *
     * @param personID
     * @param projectID
     *            can be null, in this case get the field restrictions for all
     *            roles the user has in any project
     * @param issueTypeID
     *            can be null, same as by projectID
     * @param edit
     *            whether we are in an editing (edit/create issue) mode or only
     *            read only mode (print issue, email sending)
     */
    public static Map<Integer, Integer> getFieldRestrictions(Integer personID, Integer projectID,
            Integer issueTypeID, boolean edit) {
        return getFieldRestrictions(personID, projectID, issueTypeID, null, edit);
    }

    /**
     * Returns the fields restrictions for a person for a certain project and
     * issueType A field will be returned only if it is restricted (either no
     * read or no modify right)
     *
     * @param personID
     * @param projectID
     *            can be null, in this case get the field restrictions for all
     *            roles the user has in any project
     * @param issueTypeID
     *            can be null, same as by projectID
     * @param fieldIDs
     *            can be null, if specified search for restrictions only for
     *            these fields
     * @param edit
     *            whether we are in an editing (edit/create issue) mode or only
     *            read only mode (print issue, email sending)
     * @return a map with fieldID to restriction
     */
    public static Map<Integer, Integer> getFieldRestrictions(Integer personID, Integer projectID,
            Integer issueTypeID, List<Integer> fieldIDs, boolean edit) {
        Map<Integer, Integer> resultMap = new HashMap<Integer, Integer>();
        if (personID == null) {
            // person can be null for example by POP3 email submission
            // the submitter gets an email by creating of the issue,
            // but then no restrictions are needed
            return resultMap;
        }
        // get all hidden and read only fields for all roles
        Map<Integer, Set<Integer>> hiddenFields = new HashMap<Integer, Set<Integer>>();
        Map<Integer, Set<Integer>> readOnlyFields = new HashMap<Integer, Set<Integer>>();
        FieldsRestrictionsToRoleBL.getRestrictedFieldsToRoles(fieldIDs, hiddenFields, readOnlyFields);
        if (!hasFieldRestrictions(hiddenFields, readOnlyFields, fieldIDs, edit)) {
            return resultMap;
        }
        List<Integer> personIDs = getMeAndSubstitutedAndGroups(personID);
        Set<Integer> roles = null;
        if (projectID != null) {
            // roles for project and issueType
            roles = getRolesForPersonInProjectForIssueType(personIDs, projectID, issueTypeID);
        } else {
            // roles in any projects for any issueType
            roles = getAllRolesForPerson(personIDs);
        }
        if (roles == null || roles.isEmpty()) {
            // all roles revoked from the project (only RACI role) -> no
            // roleFlag restriction
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(noRole + personID + " in project " + projectID + " issueType " + issueTypeID);
            }
            return resultMap;
        }
        if (edit) {
            Set<Integer> allRolesWithHiddenOrReadOnlyFields = new HashSet<Integer>();
            for (Set<Integer> rolesWithHiddenFields : hiddenFields.values()) {
                allRolesWithHiddenOrReadOnlyFields.addAll(rolesWithHiddenFields);
            }
            for (Set<Integer> rolesWithReadOnlyFields : readOnlyFields.values()) {
                allRolesWithHiddenOrReadOnlyFields.addAll(rolesWithReadOnlyFields);
            }
            if (allRolesWithHiddenOrReadOnlyFields.containsAll(roles)) {
                // each role a user has contains either hidden or read only
                // field restrictions
                addRestrictions(resultMap, roles, hiddenFields, TRoleFieldBean.ACCESSFLAG.NOACCESS);
                addRestrictions(resultMap, roles, readOnlyFields, TRoleFieldBean.ACCESSFLAG.READ_ONLY);
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Role without restriction found for person " + personID + " in project "
                            + projectID + " issueType " + issueTypeID);
                }
                return resultMap;
            }
        } else {
            Set<Integer> allRolesWithHiddenFields = new HashSet<Integer>();
            for (Set<Integer> rolesWithHiddenFields : hiddenFields.values()) {
                allRolesWithHiddenFields.addAll(rolesWithHiddenFields);
            }
            if (allRolesWithHiddenFields.containsAll(roles)) {
                // each role a user has contains hidden field restrictions
                addRestrictions(resultMap, roles, hiddenFields, TRoleFieldBean.ACCESSFLAG.NOACCESS);
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Role without restriction found for person " + personID + " in project "
                            + projectID + " issueType " + issueTypeID);
                }
                return resultMap;
            }
        }
        return resultMap;
    }

    /**
     * Whether any role contains fieldRestrictions
     *
     * @param hiddenFields
     * @param readOnlyFields
     * @param fieldIDs
     * @param edit
     * @return
     */
    private static boolean hasFieldRestrictions(Map<Integer, Set<Integer>> hiddenFields,
            Map<Integer, Set<Integer>> readOnlyFields, List<Integer> fieldIDs, boolean edit) {
        if (edit) {
            if (hiddenFields.isEmpty() && readOnlyFields.isEmpty()) {
                // if none of the roles in the system has hidden or read only
                // restrictions for any field
                return false;
            } else {
                if (fieldIDs != null && !hasFieldRestrictionInAnyRole(readOnlyFields, fieldIDs)
                        && !hasFieldRestrictionInAnyRole(hiddenFields, fieldIDs)) {
                    // if none of the roles in the system has hidden field or
                    // read only restrictions for fieldIDs
                    return false;
                }
            }
        } else {
            if (hiddenFields.isEmpty()) {
                // if none of the roles in the system has hidden restrictions
                // for any field
                return false;
            } else {
                if (fieldIDs != null && !hasFieldRestrictionInAnyRole(hiddenFields, fieldIDs)) {
                    // if none of the roles in the system has hidden field
                    // restrictions for fieldIDs
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Whether any of the the fieldIDs fields are restricted in any role
     *
     * @param restrictedFieldsMap
     * @param fieldIDs
     * @return
     */
    private static boolean hasFieldRestrictionInAnyRole(Map<Integer, Set<Integer>> restrictedFieldsMap,
            List<Integer> fieldIDs) {
        if (restrictedFieldsMap != null) {

            for (Integer fieldID : fieldIDs) {
                if (restrictedFieldsMap.containsKey(fieldID)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Add restrictions
     *
     * @param resultMap
     * @param roles
     * @param restrictedFieldsToRoles
     * @param restriction
     */
    static void addRestrictions(Map<Integer, Integer> resultMap, Set<Integer> roles,
            Map<Integer, Set<Integer>> restrictedFieldsToRoles, Integer restriction) {
        for (Integer fieldID : restrictedFieldsToRoles.keySet()) {
            Set<Integer> restrictedRoles = restrictedFieldsToRoles.get(fieldID);
            if (restrictedRoles != null && restrictedRoles.containsAll(roles)) {
                // field is restricted in all roles
                resultMap.put(fieldID, restriction);
            }
        }
    }

    /**
     * Gets the roles for person in project for issueType
     *
     * @param personID
     * @param projectID
     * @param issueTypeID
     * @return
     */
    private static Set<Integer> getRolesForPersonInProjectForIssueType(List<Integer> personIDs, Integer projectID,
            Integer issueTypeID) {
        Set<Integer> roles = new HashSet<Integer>();
        if (projectID == null || personIDs == null) {
            return new HashSet<Integer>();
        }
        List<Integer> ancestorProjects = ProjectBL.getAncestorProjects(projectID);
        List<TAccessControlListBean> acList = AccessControlBL.loadByPersonsAndProjects(personIDs, ancestorProjects);
        // all roles for person
        Set<Integer> allRoles = getRolesFromAcList(acList);
        if (issueTypeID == null) {
            return allRoles;
        }
        List<TRoleBean> rolesWithListType = RoleBL.loadWithExplicitIssueType(allRoles.toArray());
        // gather the roles with explicit listTypes (not necessarily
        // issueTypeID!)
        Set<Integer> roleWithListTypeSet = new HashSet<Integer>();
        if (rolesWithListType != null && !rolesWithListType.isEmpty()) {
            for (TRoleBean roleBean : rolesWithListType) {
                roleWithListTypeSet.add(roleBean.getObjectID());
            }
        }
        // gather the role without listTypes in a Set
        for (Integer roleID : allRoles) {
            if (!roleWithListTypeSet.contains(roleID)) {
                roles.add(roleID);
            }
        }
        // search for roles with explicit listTypes
        if (roleWithListTypeSet != null && !roleWithListTypeSet.isEmpty()) {
            List<TRoleListTypeBean> roleListTypeBeans = RoleBL.loadByRolesAndListType(roleWithListTypeSet.toArray(),
                    issueTypeID);
            if (roleListTypeBeans != null && !roleListTypeBeans.isEmpty()) {
                for (TRoleListTypeBean roleListTypeBean : roleListTypeBeans) {
                    roles.add(roleListTypeBean.getRole());
                }
            }
        }
        return roles;
    }

    /**
     * Gets the roles for person in any project
     *
     * @param personIDs
     * @return
     */
    private static Set<Integer> getAllRolesForPerson(List<Integer> personIDs) {
        Set<Integer> roles = new HashSet<Integer>();
        int[] projectStatusFlags = new int[] { TSystemStateBean.STATEFLAGS.ACTIVE,
                TSystemStateBean.STATEFLAGS.INACTIVE };
        if (personIDs != null && !personIDs.isEmpty()) {
            List<TAccessControlListBean> accessControlListBeans = AccessControlBL
                    .loadByPersonsAndProjectStatusFlag(personIDs, projectStatusFlags);
            return getRolesFromAcList(accessControlListBeans);
        }
        return roles;
    }

    /**
     * Whether the field is restricted in any project to issueType context
     *
     * @param personID
     * @param projectToIssueTypesMap
     * @param fieldID
     * @param edit
     * @return
     */
    public static boolean getFieldRestrictedInAnyContext(Integer personID,
            Map<Integer, Set<Integer>> projectToIssueTypesMap, Integer fieldID, boolean edit) {
        List<Integer> fieldIDs = new LinkedList<Integer>();
        fieldIDs.add(fieldID);
        Map<Integer, Map<Integer, Map<Integer, Integer>>> fieldRestrictions = AccessBeans
                .getFieldRestrictions(personID, projectToIssueTypesMap, fieldIDs, edit);
        if (fieldRestrictions != null && !fieldRestrictions.isEmpty()) {
            for (Integer projectID : projectToIssueTypesMap.keySet()) {
                Set<Integer> issueTypeIDsSet = projectToIssueTypesMap.get(projectID);
                if (issueTypeIDsSet != null) {
                    for (Integer issueTypeID : issueTypeIDsSet) {
                        Map<Integer, Map<Integer, Integer>> issueTypeRestrictions = fieldRestrictions
                                .get(projectID);
                        Map<Integer, Integer> restrictedFields = null;
                        if (issueTypeRestrictions != null) {
                            restrictedFields = issueTypeRestrictions.get(issueTypeID);
                            if (restrictedFields != null && !restrictedFields.isEmpty()) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Gets the field restrictions for a list of workItems
     *
     * @param personID
     * @param workItemBeanList
     * @param fieldIDs
     * @param edit
     * @return
     */
    public static Map<Integer, Map<Integer, Map<Integer, Integer>>> getFieldRestrictions(Integer personID,
            List<TWorkItemBean> workItemBeanList, List<Integer> fieldIDs, boolean edit) {
        Map<Integer, Set<Integer>> projectToIssueTypesMap = AccessBeans.getProjectToIssueTypesMap(workItemBeanList);
        return getFieldRestrictions(personID, projectToIssueTypesMap, fieldIDs, edit);
    }

    /**
     * Returns the fields restrictions for a person for a projects and issue
     * types By convention a field will be returned only if it is restricted
     * (either no read (and consequently no modify) or no modify right)
     *
     * @param personID
     * @param projectToIssueTypesMap
     * @param fieldIDs
     * @param edit
     *            whether we are in an editing (edit/create issue) mode or only
     *            read only mode (print issue, email sending)
     */
    public static Map<Integer, Map<Integer, Map<Integer, Integer>>> getFieldRestrictions(Integer personID,
            Map<Integer, Set<Integer>> projectToIssueTypesMap, List<Integer> fieldIDs, boolean edit) {
        Map<Integer, Map<Integer, Map<Integer, Integer>>> resultMap = new HashMap<Integer, Map<Integer, Map<Integer, Integer>>>();
        if (personID == null) {
            // person can be null for example by POP3 email submission
            // the submitter gets an email by creating of the issue,
            // but then no restrictions are needed
            return resultMap;
        }
        Map<Integer, Set<Integer>> hiddenFields = new HashMap<Integer, Set<Integer>>();
        Map<Integer, Set<Integer>> readOnlyFields = new HashMap<Integer, Set<Integer>>();
        FieldsRestrictionsToRoleBL.getRestrictedFieldsToRoles(fieldIDs, hiddenFields, readOnlyFields);
        if (!hasFieldRestrictions(hiddenFields, readOnlyFields, fieldIDs, edit)) {
            return resultMap;
        }
        /**
         * Get the role for any project/issueType combinations
         */
        Map<Integer, Map<Integer, Set<Integer>>> rolesForProjectIssueType = getRolesForPersonInProjectsForIssueTypes(
                personID, projectToIssueTypesMap);
        for (Integer projectID : projectToIssueTypesMap.keySet()) {
            Map<Integer, Map<Integer, Integer>> projectRestrictions = new HashMap<Integer, Map<Integer, Integer>>();
            resultMap.put(projectID, projectRestrictions);
            Set<Integer> issueTypesSet = projectToIssueTypesMap.get(projectID);
            Map<Integer, Set<Integer>> projectRoles = rolesForProjectIssueType.get(projectID);
            if (projectRoles != null) {
                for (Integer issueTypeID : issueTypesSet) {
                    Map<Integer, Integer> projectIssueTypeRestrictions = new HashMap<Integer, Integer>();
                    projectRestrictions.put(issueTypeID, projectIssueTypeRestrictions);
                    Set<Integer> rolesWithIssueType = projectRoles.get(issueTypeID);
                    Set<Integer> rolesWithoutIssueType = projectRoles.get(NO_ISSUETYPE_RESTRICTION);
                    Set<Integer> roles = new HashSet<Integer>();
                    if (rolesWithIssueType != null && !rolesWithIssueType.isEmpty()) {
                        roles.addAll(rolesWithIssueType);
                    }
                    if (rolesWithoutIssueType != null && !rolesWithoutIssueType.isEmpty()) {
                        roles.addAll(rolesWithoutIssueType);
                    }
                    if (!roles.isEmpty()) {
                        if (edit) {
                            Set<Integer> allRolesWithHiddenOrReadOnlyFields = new HashSet<Integer>();
                            for (Set<Integer> rolesWithHiddenFields : hiddenFields.values()) {
                                allRolesWithHiddenOrReadOnlyFields.addAll(rolesWithHiddenFields);
                            }
                            for (Set<Integer> rolesWithReadOnlyFields : readOnlyFields.values()) {
                                allRolesWithHiddenOrReadOnlyFields.addAll(rolesWithReadOnlyFields);
                            }
                            if (allRolesWithHiddenOrReadOnlyFields.containsAll(roles)) {
                                addRestrictions(projectIssueTypeRestrictions, roles, hiddenFields,
                                        TRoleFieldBean.ACCESSFLAG.NOACCESS);
                                addRestrictions(projectIssueTypeRestrictions, roles, readOnlyFields,
                                        TRoleFieldBean.ACCESSFLAG.READ_ONLY);
                            } else {
                                LOGGER.debug("Role without restriction found for person " + personID + " project "
                                        + projectID + " and issueType " + issueTypeID);
                                return resultMap;
                            }
                        } else {
                            Set<Integer> allRolesWithHiddenFields = new HashSet<Integer>();
                            for (Set<Integer> rolesWithHiddenFields : hiddenFields.values()) {
                                allRolesWithHiddenFields.addAll(rolesWithHiddenFields);
                            }
                            if (allRolesWithHiddenFields.containsAll(roles)) {
                                addRestrictions(projectIssueTypeRestrictions, roles, hiddenFields,
                                        TRoleFieldBean.ACCESSFLAG.NOACCESS);
                            } else {
                                LOGGER.debug("Role without restriction found for person " + personID + " project "
                                        + projectID + " and issueType " + issueTypeID);
                                return resultMap;
                            }
                        }
                    } else {
                        LOGGER.debug(
                                noRole + personID + " in project " + projectID + " and issueType" + issueTypeID);
                    }
                }
            } else {
                LOGGER.debug(noRole + personID + " in project " + projectID);
            }
        }
        return resultMap;
    }

    private static Integer NO_ISSUETYPE_RESTRICTION = Integer.valueOf(-100);

    /**
     * Gets the roles for person in project for issueType
     *
     * @param personID
     * @param projectToIssueTypesMap
     * @return
     */
    private static Map<Integer, Map<Integer, Set<Integer>>> getRolesForPersonInProjectsForIssueTypes(
            Integer personID, Map<Integer, Set<Integer>> projectToIssueTypesMap) {
        if (projectToIssueTypesMap == null || projectToIssueTypesMap.isEmpty() || personID == null) {
            return new HashMap<Integer, Map<Integer, Set<Integer>>>();
        }
        /**
         * Gets the ancestor projects hierarchy as a map
         */
        Map<Integer, Integer> ancestorProjectHierarchy = ProjectBL.getAncestorProjectHierarchy(
                GeneralUtils.createIntegerArrFromCollection(projectToIssueTypesMap.keySet()));
        /**
         * Get the projects and ancestor projects set
         */
        Set<Integer> allProjectsSet = ancestorProjectHierarchy.keySet();
        List<Integer> personIDs = getMeAndSubstitutedAndGroups(personID);
        /**
         * Gets the access control beans for all projects (direct and ancestor)
         */
        List<TAccessControlListBean> acList = AccessControlBL.loadByPersonsAndProjects(personIDs,
                GeneralUtils.createListFromCollection(allProjectsSet));
        // all involved roles for person
        Set<Integer> allRoles = getRolesFromAcList(acList);
        /**
         * Get the list type set for each involved role
         */
        Map<Integer, Set<Integer>> rolesToListTypes = new HashMap<Integer, Set<Integer>>();
        List<TRoleListTypeBean> roleListTypeBeans = RoleBL.loadByRolesAndListType(allRoles.toArray(), null);
        if (roleListTypeBeans != null && !roleListTypeBeans.isEmpty()) {
            for (TRoleListTypeBean roleListTypeBean : roleListTypeBeans) {
                Integer roleID = roleListTypeBean.getRole();
                Integer issueType = roleListTypeBean.getListType();
                Set<Integer> issueTypes = rolesToListTypes.get(roleID);
                if (issueTypes == null) {
                    issueTypes = new HashSet<Integer>();
                    rolesToListTypes.put(roleID, issueTypes);
                }
                issueTypes.add(issueType);
            }
        }
        /**
         * Build up the result map
         */
        Map<Integer, Map<Integer, Set<Integer>>> resultMap = new HashMap<Integer, Map<Integer, Set<Integer>>>();
        /**
         * Get the roles set for each involved project
         */
        Map<Integer, Set<Integer>> rolesForProjects = new HashMap<Integer, Set<Integer>>();
        for (TAccessControlListBean accessControlListBean : acList) {
            Integer projectID = accessControlListBean.getProjectID();
            Integer roleID = accessControlListBean.getRoleID();
            Set<Integer> roleIDs = rolesForProjects.get(projectID);
            if (roleIDs == null) {
                roleIDs = new HashSet<Integer>();
                rolesForProjects.put(projectID, roleIDs);
            }
            roleIDs.add(roleID);
        }
        /**
         * For each possible project/issueType gather the roles
         */
        for (Integer projectID : projectToIssueTypesMap.keySet()) {
            // get direct roles in project
            Set<Integer> roleIDs = rolesForProjects.get(projectID);
            if (roleIDs == null) {
                // no direct role for project
                roleIDs = new HashSet<Integer>();
            }
            Integer parentProject = ancestorProjectHierarchy.get(projectID);
            while (parentProject != null) {
                // add the roles from ancestor projects
                Set<Integer> parentRoles = rolesForProjects.get(parentProject);
                if (parentRoles != null) {
                    roleIDs.addAll(parentRoles);
                }
                parentProject = ancestorProjectHierarchy.get(parentProject);
            }
            Map<Integer, Set<Integer>> projectRolesMap = new HashMap<Integer, Set<Integer>>();
            resultMap.put(projectID, projectRolesMap);
            for (Integer roleID : roleIDs) {
                if (rolesToListTypes.containsKey(roleID)) {
                    // role with issue type(s)
                    Set<Integer> issueTypesForRole = rolesToListTypes.get(roleID);
                    for (Integer issueTypeID : issueTypesForRole) {
                        Set<Integer> rolesForProjectAndIssueType = projectRolesMap.get(issueTypeID);
                        if (rolesForProjectAndIssueType == null) {
                            rolesForProjectAndIssueType = new HashSet<Integer>();
                            projectRolesMap.put(issueTypeID, rolesForProjectAndIssueType);
                        }
                        rolesForProjectAndIssueType.add(roleID);
                    }
                } else {
                    // role without issue type restrictions
                    Set<Integer> rolesForProjectAndIssueType = projectRolesMap.get(NO_ISSUETYPE_RESTRICTION);
                    if (rolesForProjectAndIssueType == null) {
                        rolesForProjectAndIssueType = new HashSet<Integer>();
                        projectRolesMap.put(NO_ISSUETYPE_RESTRICTION, rolesForProjectAndIssueType);
                    }
                    rolesForProjectAndIssueType.add(roleID);
                }
            }
        }
        return resultMap;
    }

    /****************************************** Role set for persons in projects ******************************************/

    /**
     * Gets the list of accessControlListBeans by person (including groups the
     * person is member of), project (also ancestor projects) and right flags
     *
     * @param personID
     * @param projectID
     * @param rights
     * @return
     */
    private static List<TAccessControlListBean> loadByPersonProjectsRight(Integer personID, Integer projectID,
            int[] rights, boolean includeSubstituted) {
        List<Integer> ancestorProjects = ProjectBL.getAncestorProjects(projectID);
        List<Integer> personIDs = null;
        if (includeSubstituted) {
            personIDs = getMeAndSubstitutedAndGroups(personID);
        } else {
            personIDs = getMeAndMyGroups(personID);
        }
        return AccessControlBL.loadByPersonProjectsRight(personIDs, ancestorProjects, rights);
    }

    /**
     * Gets the list of accessControlListBeans by person (including groups the
     * person is member of), project (also ancestor projects) and right flags
     *
     * @param personID
     * @param projectIDs
     * @param rights
     * @return
     */
    public static List<TAccessControlListBean> loadByPersonAndRight(Integer personID, int[] rights,
            boolean includeSubstituted) {
        List<Integer> personIDs;
        if (includeSubstituted) {
            personIDs = getMeAndSubstitutedAndGroups(personID);
        } else {
            personIDs = getMeAndMyGroups(personID);
        }
        return AccessControlBL.loadByPersonRightInAnyProjectWithStatusFlag(personIDs, rights,
                new int[] { TSystemStateBean.STATEFLAGS.ACTIVE, TSystemStateBean.STATEFLAGS.INACTIVE });
    }

    /**
     * Gets the roles with right for persons in a project
     *
     * @param personID
     * @param projectID
     * @param arrRights
     * @return
     */
    public static Set<Integer> getRolesWithRightForPersonInProject(Integer personID, Integer projectID,
            int[] arrRights) {
        if (projectID == null || personID == null) {
            return new HashSet<Integer>();
        }
        List<TAccessControlListBean> acList = loadByPersonProjectsRight(personID, projectID, arrRights, true);
        return AccessBeans.getRolesFromAcList(acList);
    }

    /**
     * Gets the status IDs assigned to the projectType for an issueType
     *
     * @param projectID
     * @param rights
     * @return
     */
    public static Set<Integer> getRolesSetByProjectRights(Integer projectID, int[] rights) {
        Integer[] projectIDs = GeneralUtils
                .createIntegerArrFromCollection(ProjectBL.getAncestorProjects(projectID));
        List<TAccessControlListBean> accessControlListBeans = AccessControlBL.loadByProjectsAndRights(projectIDs,
                rights);
        return getRolesFromAcList(accessControlListBeans);
    }

    /**
     * Gets the distinct roleIDs from a list of TAccessControlListBean beans
     *
     * @param acList
     * @return
     */
    private static Set<Integer> getRolesFromAcList(List<TAccessControlListBean> acList) {
        Set<Integer> roleSet = new HashSet<Integer>();
        if (acList != null) {
            for (TAccessControlListBean accessControlListBean : acList) {
                roleSet.add(accessControlListBean.getRoleID());
            }
        }
        return roleSet;
    }

    /**
     * Returns whether a person has a specific right in a project for a listType
     *
     * @param personID
     * @param projectID
     * @return
     */
    public static boolean isPersonProjectAdminForProject(Integer personID, Integer projectID,
            boolean includeSubstitutedPersons) {
        if (projectID == null || personID == null) {
            return false;
        }
        List<TAccessControlListBean> acList = loadByPersonProjectsRight(personID, projectID,
                new int[] { AccessFlagIndexes.PROJECTADMIN }, includeSubstitutedPersons);
        // no right found at all
        if (acList == null || acList.isEmpty()) {
            return false;
        }
        return true;
    }

    /**
     * Returns whether a person has a specific right in a project for a listType
     *
     * @param personID
     * @param projectID
     * @param issueTypeID
     *            if null, it does not matter (for example project admin role
     *            doesn't depend on list type)
     * @param right
     *            the right flag
     * @param projectAdmin
     *            whether as project admin she has the right automatically
     * @return
     */
    public static boolean hasPersonRightInProjectForIssueType(Integer personID, Integer projectID,
            Integer issueTypeID, int right, boolean projectAdmin, boolean includeSubstitutedPersons) {
        if (projectID == null || personID == null) {
            return false;
        }
        int[] arrRights;
        if (projectAdmin && right != AccessFlagIndexes.PROJECTADMIN) {
            arrRights = new int[] { right, AccessFlagIndexes.PROJECTADMIN };
        } else {
            arrRights = new int[] { right };
        }
        List<TAccessControlListBean> acList = loadByPersonProjectsRight(personID, projectID, arrRights,
                includeSubstitutedPersons);
        // no right found at all
        if (acList == null || acList.isEmpty()) {
            return false;
        }
        // list type does not matter
        if (issueTypeID == null) {
            return true;
        }
        // Gather the found roleIDs in a Set because one could have the same
        // right direct and through groups
        Set<Integer> roles = getRolesFromAcList(acList);

        Object[] roleIDs = roles.toArray();

        // does at least one right have an implicit access to the list type?
        List<TRoleBean> issueTypeRoles = RoleBL.loadWithExplicitIssueType(roleIDs);
        // at least one right has no restrictions for listType
        if (issueTypeRoles != null && issueTypeRoles.size() < roles.size()) {
            return true;
        }
        // search for explicit rights for a listType
        List<TRoleListTypeBean> roleListTypeBeans = RoleBL.loadByRolesAndListType(roleIDs, issueTypeID);
        return roleListTypeBeans != null && !roleListTypeBeans.isEmpty();
    }

    /**
     * Returns whether a person has a specific right in a project for a listType
     *
     * @param personID
     * @param projectID
     * @param issueTypeID
     *            if null, it does not matter (for example project admin role
     *            doesn't depend on list type)
     * @param role
     * @return
     */
    public static boolean hasPersonRoleInProjectForIssueType(Integer personID, Integer projectID,
            Integer issueTypeID, Integer role) {
        if (projectID == null || personID == null) {
            return false;
        }
        List<TAccessControlListBean> acList = loadByPersonProjectsRole(personID, projectID, role);
        // no right found at all
        if (acList == null || acList.isEmpty()) {
            return false;
        }
        // list type does not matter
        if (issueTypeID == null) {
            return true;
        }
        // Gather the found roleIDs in a Set because one could have the same
        // right direct and through groups
        Set<Integer> roles = getRolesFromAcList(acList);
        Object[] roleIDs = roles.toArray();
        // does at least one right have an implicit access to the list type?
        List<TRoleBean> issueTypeRoles = RoleBL.loadWithExplicitIssueType(roleIDs);
        // at least one right has no restrictions for listType
        if (issueTypeRoles != null && issueTypeRoles.size() < roles.size()) {
            return true;
        }
        // search for explicit rights for a listType
        List<TRoleListTypeBean> roleListTypeBeans = RoleBL.loadByRolesAndListType(roleIDs, issueTypeID);
        return roleListTypeBeans != null && !roleListTypeBeans.isEmpty();
    }

    /**
     * Gets the list of accessControlListBeans by person (including groups the
     * person is member of), project (also ancestor projects) and role
     *
     * @param personID
     * @param projectID
     * @param role
     * @return
     */
    private static List<TAccessControlListBean> loadByPersonProjectsRole(Integer personID, Integer projectID,
            Integer role) {
        List<Integer> ancestorProjects = ProjectBL.getAncestorProjects(projectID);
        List<Integer> personIDs = getMeAndSubstitutedAndGroups(personID);
        return AccessControlBL.loadByPersonProjectsRole(personIDs, ancestorProjects, role);
    }

    /**
     * Gets the "projectsIDs to issueTypeID set" map the person has rights in
     *
     * @param personIDs
     * @param projects
     * @param arrRights
     * @return
     */
    public static Map<Integer, Set<Integer>> getProjectsToIssueTypesWithRoleForPerson(List<Integer> personIDs,
            Integer[] projects, int[] arrRights) {
        List<Record> assignedProjectAndIssueTypeRecords = AccessControlBL.getProjectIssueTypeRecords(personIDs,
                projects, arrRights);
        Map<Integer, Set<Integer>> projectIssueTypeMap = new HashMap<Integer, Set<Integer>>();
        for (Record record : assignedProjectAndIssueTypeRecords) {
            try {
                Integer projectID = record.getValue(1).asIntegerObj();
                Integer issueTypeID = null;
                if (record.getValue(2) != null) {
                    issueTypeID = record.getValue(2).asIntegerObj();
                }
                // check if projectID is already a key
                Set<Integer> issueTypeSet = projectIssueTypeMap.get(projectID);
                if (issueTypeSet == null) {
                    // create new HashSet for this key
                    issueTypeSet = new HashSet<Integer>();
                    projectIssueTypeMap.put(projectID, issueTypeSet);
                }
                // add the issueTypeID. It could be null which
                // means no issueType restrictions for that project
                issueTypeSet.add(issueTypeID);
            } catch (DataSetException e) {
                LOGGER.error("Reading the result record failed with " + e.getMessage());
                LOGGER.debug(ExceptionUtils.getStackTrace(e));
            }
        }
        return projectIssueTypeMap;
    }

    /**
     * Verifies if a person has one of the specific rights for a list of
     * workItemBeans. Specific rights means either a role with any arrRights or
     * a RACI role If the person does not have the right, the workItemBean is
     * removed form the list
     *
     * @param personID
     *            , the id of the person
     * @param workItemBeans
     *            , list of reportBean which needs o be checked
     * @param editFlagNeeded
     *            whether the edit flag should be set for workItems
     *
     */
    public static List<TWorkItemBean> filterWorkItemBeans(Integer personID, List<TWorkItemBean> workItemBeans,
            boolean editFlagNeeded) {
        List<TWorkItemBean> workItemBeansPassed = new LinkedList<TWorkItemBean>();
        Set<Integer> workItemIDsPassed = new HashSet<Integer>();
        Map<Integer, TWorkItemBean> notEditableMap = new HashMap<Integer, TWorkItemBean>();
        Date start = null;
        if (LOGGER.isInfoEnabled()) {
            start = new Date();
        }
        if (personID == null || workItemBeans == null || workItemBeans.isEmpty()) {
            LOGGER.debug("PersonID " + personID + " and number of items " + workItemBeans.size());
            return new LinkedList<TWorkItemBean>();
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Person " + personID + ": total number of items before filter " + workItemBeans.size());
        }
        List<Integer> meAndSubstitutedIDs = getMeAndSubstituted(personID);
        /**
         * First loop: pass the direct RACI (my) items
         */
        Iterator<TWorkItemBean> workItemBeansItr = workItemBeans.iterator();
        while (workItemBeansItr.hasNext()) {
            TWorkItemBean workItemBean = workItemBeansItr.next();
            Integer workItemID = workItemBean.getObjectID();
            Integer originatorID = workItemBean.getOriginatorID();
            if (workItemBean.isAccessLevelFlag() && !originatorID.equals(personID)) {
                // check the access level (private) flag: independently of any
                // other assigned roles or RACI roles
                // a workItem with accessLevel set to ACCESS_LEVEL_PRIVATE is
                // readable only when the person logged is the originator
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Remove item " + workItemID + ": private issue");
                }
                workItemBeansItr.remove();
                continue;
            }
            Integer responsibleID = workItemBean.getResponsibleID();
            Integer managerID = workItemBean.getOwnerID();
            // pass the workItem and set as editable if it is my item
            if (meAndSubstitutedIDs.contains(originatorID) || meAndSubstitutedIDs.contains(managerID)
                    || meAndSubstitutedIDs.contains(responsibleID)) {
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace(addItem + workItemID + ": direct RACI");
                }
                if (editFlagNeeded) {
                    workItemBean.setEditable(true);
                }
                workItemBeansPassed.add(workItemBean);
                workItemIDsPassed.add(workItemBean.getObjectID());
                workItemBeansItr.remove();
            }
        }
        int noOfDirectRACIItems = workItemBeansPassed.size();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Number of direct RACI items: " + noOfDirectRACIItems + ", " + workItemBeans.size()
                    + remaining);
        }
        /**
         * Second loop: pass by project roles for person
         */
        List<Integer> meAndSubstitutedAndGroups = null;
        int noOfProjectRightItems = 0;
        if (!workItemBeans.isEmpty()) {
            meAndSubstitutedAndGroups = getMeAndSubstitutedAndGroups(personID);
            Set<Integer> projectsDirectlyInvolved = new HashSet<Integer>();
            for (TWorkItemBean workItemBean : workItemBeans) {
                Integer projectID = workItemBean.getProjectID();
                projectsDirectlyInvolved.add(projectID);
            }
            List<TProjectBean> projectBeansInvolved = ProjectBL
                    .loadByProjectIDs(GeneralUtils.createListFromSet(projectsDirectlyInvolved));
            Map<Integer, TProjectBean> involvedProjectsMap = new HashMap<Integer, TProjectBean>();
            Map<Integer, Integer> childToParentProject = ProjectBL.getChildToParentMap(projectBeansInvolved,
                    involvedProjectsMap);
            Integer[] ancestorProjects = GeneralUtils.createIntegerArrFromCollection(involvedProjectsMap.keySet());
            // get the array with groupIDs the person is member of (the personID
            // is also contained)

            // projects-issueTypes with project admin right for person

            // projects/issueTypes with projectAdmin roles

            // projects/issueTypes with read any roles

            int[] readAnyRights = new int[] { AccessFlagIndexes.READANYTASK, AccessFlagIndexes.PROJECTADMIN };
            // projects-issueTypes with read right for person
            Map<Integer, Set<Integer>> projectToIssueTypesWithReadRight = getProjectsToIssueTypesWithRoleForPerson(
                    meAndSubstitutedAndGroups, ancestorProjects, readAnyRights);
            // projects-issueTypes with edit right for person
            Map<Integer, Set<Integer>> projectToIssueTypesWithEditRight = null;
            if (editFlagNeeded) {
                int[] editAnyRights;
                editAnyRights = new int[] { AccessFlagIndexes.MODIFYANYTASK, AccessFlagIndexes.PROJECTADMIN };
                projectToIssueTypesWithEditRight = getProjectsToIssueTypesWithRoleForPerson(
                        meAndSubstitutedAndGroups, ancestorProjects, editAnyRights);
            }
            workItemBeansItr = workItemBeans.iterator();
            while (workItemBeansItr.hasNext()) {
                TWorkItemBean workItemBean = workItemBeansItr.next();
                Integer workItemID = workItemBean.getObjectID();
                Integer projectID = workItemBean.getProjectID();
                Integer issueType = workItemBean.getListTypeID();
                // leave the archived/deleted workItems only if the person is
                // project admin
                if (editFlagNeeded && hasExplicitRight(personID, workItemID, projectID, issueType,
                        projectToIssueTypesWithEditRight, childToParentProject, "edit")) {
                    // explicit edit right: pass and set as editable
                    if (LOGGER.isTraceEnabled()) {
                        String projectName = null;
                        ILabelBean projectBean = LookupContainer.getProjectBean(projectID);
                        if (projectBean != null) {
                            projectName = projectBean.getLabel();
                        }
                        LOGGER.trace(addItem + workItemID + " for user " + personID
                                + ": 'modify any' project right for project " + projectID + " (" + projectName
                                + ")");
                    }
                    if (!workItemIDsPassed.contains(workItemBean.getObjectID())) {
                        workItemIDsPassed.add(workItemBean.getObjectID());
                        workItemBeansPassed.add(workItemBean);
                        workItemBean.setEditable(true);
                    }
                    workItemBeansItr.remove();
                } else {
                    if (hasExplicitRight(personID, workItemID, projectID, issueType,
                            projectToIssueTypesWithReadRight, childToParentProject, "read")) {
                        // explicit read right: pass but do not set as editable
                        if (!workItemIDsPassed.contains(workItemBean.getObjectID())) {
                            workItemIDsPassed.add(workItemBean.getObjectID());
                            workItemBeansPassed.add(workItemBean);
                            if (LOGGER.isTraceEnabled()) {
                                String projectName = null;
                                ILabelBean projectBean = LookupContainer.getProjectBean(projectID);
                                if (projectBean != null) {
                                    projectName = projectBean.getLabel();
                                }
                                LOGGER.trace(addItem + workItemID + ": 'read any' project right for project "
                                        + projectID + " (" + projectName + ")");
                            }
                        }
                        if (editFlagNeeded) {
                            // although it is readable but not editable though
                            // project level roles: if edit flag is needed do
                            // not remove
                            // to remain in set by searching for indirect RACI
                            // and possibly set it editable later
                            notEditableMap.put(workItemID, workItemBean);
                        } else {
                            // remove to do not take care about in next loops
                            workItemBeansItr.remove();
                        }
                    }
                }
            }
            noOfProjectRightItems = workItemBeansPassed.size() - noOfDirectRACIItems;
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Number of items in projects with role for person: " + noOfProjectRightItems + ", "
                        + workItemBeans.size() + remaining);
            }
        }
        /**
         * Third loop: pass by indirect RACI persons: responsible through group,
         * reciprocal RACI through group membership in the same group
         */
        int noOfIndirectRACIItems = 0;
        Set<Integer> reciprocOriginators = null;
        if (!workItemBeans.isEmpty()) {
            Map<Integer, List<Integer>> reciprocGroups = getRaciprocGroupsMap(meAndSubstitutedIDs);
            List<Integer> allGroupsForPerson = reciprocGroups.get(ALL_PERSONGROUPS);
            List<TPersonBean> originatorsInReciprocGroups = PersonBL
                    .getIndirectPersons(reciprocGroups.get(SystemFields.ORIGINATOR), false, null);
            reciprocOriginators = GeneralUtils.createIntegerSetFromIntegerList(
                    GeneralUtils.createIntegerListFromBeanList(originatorsInReciprocGroups));
            List<TPersonBean> managersInReciprocGroups = PersonBL
                    .getIndirectPersons(reciprocGroups.get(SystemFields.MANAGER), false, null);
            Set<Integer> reciprocManagers = GeneralUtils.createIntegerSetFromIntegerList(
                    GeneralUtils.createIntegerListFromBeanList(managersInReciprocGroups));
            List<TPersonBean> responsiblesInReciprocGroups = PersonBL
                    .getIndirectPersons(reciprocGroups.get(SystemFields.RESPONSIBLE), false, null);
            Set<Integer> reciprocResponsibles = GeneralUtils.createIntegerSetFromIntegerList(
                    GeneralUtils.createIntegerListFromBeanList(responsiblesInReciprocGroups));
            workItemBeansItr = workItemBeans.iterator();
            while (workItemBeansItr.hasNext()) {
                TWorkItemBean workItemBean = workItemBeansItr.next();
                Integer workItemID = workItemBean.getObjectID();
                Integer originatorID = workItemBean.getOriginatorID();
                Integer responsibleID = workItemBean.getResponsibleID();
                Integer managerID = workItemBean.getOwnerID();
                if ((allGroupsForPerson != null && allGroupsForPerson.contains(responsibleID))
                        || reciprocOriginators.contains(originatorID) || reciprocManagers.contains(managerID)
                        || reciprocResponsibles.contains(responsibleID)) {
                    // indirect RACI: set as editable
                    if (LOGGER.isTraceEnabled()) {
                        if (allGroupsForPerson != null && allGroupsForPerson.contains(responsibleID)) {
                            LOGGER.trace(addItem + workItemID + ": responsible through group");
                        } else {
                            if (reciprocOriginators.contains(originatorID)) {
                                LOGGER.trace(addItem + workItemID + ": reciproc originator through group");
                            } else {
                                if (reciprocManagers.contains(managerID)) {
                                    LOGGER.trace(addItem + workItemID + ": reciproc manager through group");
                                } else {
                                    if (reciprocResponsibles.contains(responsibleID)) {
                                        LOGGER.trace(addItem + workItemID + ": reciproc responsible through group");
                                    }
                                }
                            }
                        }
                    }
                    // set it to editable anyway but add to the passed list if
                    // it was not yet added as read only (to avoid adding the
                    // item two times)
                    workItemBeansItr.remove();
                    if (!workItemIDsPassed.contains(workItemBean.getObjectID())) {
                        workItemIDsPassed.add(workItemBean.getObjectID());
                        workItemBeansPassed.add(workItemBean);
                    }
                    if (editFlagNeeded) {
                        workItemBean.setEditable(true);
                        if (notEditableMap.containsKey(workItemID)) {
                            notEditableMap.remove(workItemID);
                        }
                    }
                }
            }
            noOfIndirectRACIItems = workItemBeansPassed.size() - noOfDirectRACIItems - noOfProjectRightItems;
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Number of indirect RACI items: " + noOfIndirectRACIItems + ", " + workItemBeans.size()
                        + remaining);
            }
        }
        /**
         * Fourth loop: pass by watcher person
         */
        int noOfWatcherItems = 0;
        if (!workItemBeans.isEmpty()) {
            List<Integer> workItemIDs = GeneralUtils.createIntegerListFromBeanList(workItemBeans);
            List<TNotifyBean> watcherBeans = ConsInfBL.getByWorkItemsAndPersons(workItemIDs,
                    meAndSubstitutedAndGroups);
            if (watcherBeans != null && !watcherBeans.isEmpty()) {
                Set<Integer> consultedPersons = new HashSet<Integer>();
                Set<Integer> informedPersons = new HashSet<Integer>();
                for (TNotifyBean notifyBean : watcherBeans) {
                    Integer workItemID = notifyBean.getWorkItem();
                    String raciRole = notifyBean.getRaciRole();
                    if (raciRole != null) {
                        if (RaciRole.CONSULTANT.equals(raciRole)) {
                            consultedPersons.add(workItemID);
                        } else {
                            if (RaciRole.INFORMANT.equals(raciRole)) {
                                informedPersons.add(workItemID);
                            }
                        }
                    }
                }
                workItemBeansItr = workItemBeans.iterator();
                while (workItemBeansItr.hasNext()) {
                    TWorkItemBean workItemBean = workItemBeansItr.next();
                    Integer workItemID = workItemBean.getObjectID();
                    if (consultedPersons.contains(workItemID)) {
                        // consulted: pass and set editable
                        if (LOGGER.isTraceEnabled()) {
                            LOGGER.trace(addItem + workItemID + ": consulted");
                        }
                        // set it to editable anyway but add to the passed list
                        // if it was not yet added as read only (to avoid adding
                        // the item two times)
                        workItemBeansItr.remove();
                        if (!workItemIDsPassed.contains(workItemBean.getObjectID())) {
                            workItemIDsPassed.add(workItemBean.getObjectID());
                            workItemBeansPassed.add(workItemBean);
                        }
                        if (editFlagNeeded) {
                            workItemBean.setEditable(true);
                            if (notEditableMap.containsKey(workItemID)) {
                                notEditableMap.remove(workItemID);
                            }
                        }
                    } else {
                        // not already "only" readable through project roles
                        if (informedPersons.contains(workItemID)) {
                            if (!workItemIDsPassed.contains(workItemBean.getObjectID())) {
                                workItemIDsPassed.add(workItemBean.getObjectID());
                                workItemBeansPassed.add(workItemBean);
                            }
                            // informed: pass but do not set as editable
                            // although it is readable but not editable: if edit
                            // flag is needed do not remove
                            // to remain in set by searching for indirect RACI
                            // and possibly set it editable later
                            if (LOGGER.isTraceEnabled()) {
                                LOGGER.trace(addItem + workItemID + ": informed");
                            }

                            if (editFlagNeeded) {
                                // although it is readable but not editable: if
                                // edit flag is needed do not remove
                                // to remain in set by searching for indirect
                                // RACI and possibly set it editable later
                                notEditableMap.put(workItemID, workItemBean);
                            } else {
                                // remove to do not take care about in next
                                // loops
                                workItemBeansItr.remove();
                            }
                        }

                    }
                }
            }
            noOfWatcherItems = workItemBeansPassed.size() - noOfDirectRACIItems - noOfProjectRightItems
                    - noOfIndirectRACIItems;
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(
                        "Number of watcher items " + noOfWatcherItems + ", " + workItemBeans.size() + remaining);
            }
        }
        /**
         * Fifth loop: pass by on behalf persons
         */
        int noOfOnBehalfItems = 0;
        if (!workItemBeans.isEmpty()) {
            List<Integer> workItemIDs = GeneralUtils.createIntegerListFromBeanList(workItemBeans);
            List<TAttributeValueBean> userPickerAttributes = AttributeValueBL
                    .getUserPickerAttributesByWorkItems(workItemIDs);
            Map<Integer, List<TAttributeValueBean>> userPickerMap = null;
            if (userPickerAttributes != null && !userPickerAttributes.isEmpty()) {
                userPickerMap = new HashMap<Integer, List<TAttributeValueBean>>();
                for (TAttributeValueBean attributeValueBean : userPickerAttributes) {
                    Integer workItemID = attributeValueBean.getWorkItem();
                    List<TAttributeValueBean> attributeValueBeansList = userPickerMap.get(workItemID);
                    if (attributeValueBeansList == null) {
                        attributeValueBeansList = new LinkedList<TAttributeValueBean>();
                        userPickerMap.put(workItemID, attributeValueBeansList);
                    }
                    attributeValueBeansList.add(attributeValueBean);
                }
                workItemBeansItr = workItemBeans.iterator();
                while (workItemBeansItr.hasNext()) {
                    TWorkItemBean workItemBean = workItemBeansItr.next();
                    Integer workItemID = workItemBean.getObjectID();
                    Set<Integer> onBehalfOfSet = null;
                    List<TAttributeValueBean> attributeValueBeansList = userPickerMap.get(workItemID);
                    if (attributeValueBeansList != null) {
                        for (TAttributeValueBean attributeValueBean : attributeValueBeansList) {
                            Integer fieldID = attributeValueBean.getField();
                            IFieldTypeRT fieldTypeRT = FieldTypeManager.getFieldTypeRT(fieldID);
                            if (fieldTypeRT.isUserPicker()) {
                                UserPickerRT userPickerRT = (UserPickerRT) fieldTypeRT;
                                if (userPickerRT.inheritsOriginatorRole()) {
                                    if (onBehalfOfSet == null) {
                                        onBehalfOfSet = new HashSet<Integer>();
                                    }
                                    onBehalfOfSet.add(attributeValueBean.getSystemOptionID());
                                }
                            }
                        }
                    }
                    Set<Integer> onBehalfPersonsAll = new HashSet<Integer>();
                    Set<Integer> onBehalfPersonsDirect = getOnBehalfPersonsDirect(onBehalfOfSet,
                            onBehalfPersonsAll);
                    if (hasCommon(meAndSubstitutedIDs, onBehalfPersonsAll)
                            || hasCommon(reciprocOriginators, onBehalfPersonsDirect)) {
                        // indirect RACI: set as editable
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug(addItem + workItemID + ": on behalf of");
                        }
                        // set it to editable anyway but add to the passed list
                        // if it was not yet added as read only (to avoid adding
                        // the item two times)
                        workItemBeansItr.remove();
                        if (!workItemIDsPassed.contains(workItemBean.getObjectID())) {
                            workItemIDsPassed.add(workItemBean.getObjectID());
                            workItemBeansPassed.add(workItemBean);
                        }
                        if (editFlagNeeded) {
                            workItemBean.setEditable(true);
                            if (notEditableMap.containsKey(workItemID)) {
                                notEditableMap.remove(workItemID);
                            }
                        }
                    }
                }
            }
            noOfOnBehalfItems = workItemBeansPassed.size() - noOfDirectRACIItems - noOfProjectRightItems
                    - noOfIndirectRACIItems - noOfWatcherItems;
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Number of in behalf of person items " + noOfOnBehalfItems + ", "
                        + workItemBeans.size() + remaining);
            }
        }
        if (LOGGER.isDebugEnabled()) {
            // workItemBeans contain the
            // 1. not readable and
            // 2. might contain also the only readable but not editable items
            // (these items are passed already)
            // The difference is the really not readable items
            LOGGER.debug("Number of items remained with no read right "
                    + (workItemBeans.size() - notEditableMap.size()));
            LOGGER.debug("Number of items passed " + workItemBeansPassed.size());
            Date end = new Date();
            LOGGER.debug("Removing not accessible items lasted " + Long.toString(end.getTime() - start.getTime())
                    + " ms");
        }
        return workItemBeansPassed;
    }

    /**
     * Has common persons
     *
     * @param reciprocPersonsSet
     * @param onBehalfOfSet
     * @return
     */
    private static boolean hasCommon(Collection<Integer> reciprocPersonsSet, Set<Integer> onBehalfOfSet) {
        if (reciprocPersonsSet == null || onBehalfOfSet == null) {
            return false;
        }
        for (Integer personID : onBehalfOfSet) {
            if (reciprocPersonsSet.contains(personID)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Whether a workItem belongs to a project or project/issueType with an
     * explicit right
     *
     * @param personID
     * @param workItemID
     * @param projectID
     * @param issueType
     * @param projectIssueTypeMap
     * @param childToParentProject
     * @return
     */
    public static boolean hasExplicitRight(Integer personID, Integer workItemID, Integer projectID,
            Integer issueType, Map<Integer, Set<Integer>> projectIssueTypeMap,
            Map<Integer, Integer> childToParentProject, String rightName) {
        if (projectIssueTypeMap == null || projectID == null) {
            return false;
        }
        // get the Set for list-types for project:
        Set<Integer> listSet = projectIssueTypeMap.get(projectID);
        if (listSet != null && (listSet.contains(issueType) || listSet.contains(null))) {
            return true;
        } else {
            Integer parentProject = childToParentProject.get(projectID);
            if (parentProject == null) {
                if (LOGGER.isTraceEnabled()) {
                    String projectName = null;
                    TProjectBean projectBean = LookupContainer.getProjectBean(projectID);
                    if (projectBean != null) {
                        projectName = projectBean.getLabel();
                    }
                    String issueTypeName = null;
                    if (issueType != null) {
                        TListTypeBean issueTypeBean = LookupContainer.getItemTypeBean(issueType);
                        if (issueTypeBean != null) {
                            issueTypeName = issueTypeBean.getLabel();
                        }
                    }
                    LOGGER.trace("User " + personID + " has no " + rightName + " right for item " + workItemID
                            + " in project " + projectID + " (" + projectName + ")" + " issueType " + issueType
                            + " (" + issueTypeName + ")");
                }
                return false;
            } else {
                return hasExplicitRight(personID, workItemID, parentProject, issueType, projectIssueTypeMap,
                        childToParentProject, rightName);
            }
        }
    }

    /**
     * Gets the item type limitations for a project directly or inherited from
     * ancestor projects
     *
     * @param projectID
     * @param selectedItemTypeIDsSet
     * @param projectIssueTypeMap
     * @param childToParentProjectIDMap
     * @return
     */
    public static Set<Integer> getItemTypeLimitations(Integer projectID, Set<Integer> selectedItemTypeIDsSet,
            Map<Integer, Set<Integer>> projectIssueTypeMap, Map<Integer, Integer> childToParentProjectIDMap) {
        if (projectIssueTypeMap == null || projectID == null) {
            return null;
        }
        String projectLabel = null;
        if (LOGGER.isDebugEnabled()) {
            projectLabel = LookupContainer.getNotLocalizedLabelBeanLabel(SystemFields.INTEGER_PROJECT, projectID);
        }
        // get the Set for list-types for project:
        Set<Integer> roleRestrictedItemTypeIDSet = projectIssueTypeMap.get(projectID);
        if (roleRestrictedItemTypeIDSet != null) {
            if (roleRestrictedItemTypeIDSet.contains(null)) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(theProject + projectLabel + " has role without item type limitation");
                }
                // no limitation on item types not needed to look at the parent
                // projects' roles
                return roleRestrictedItemTypeIDSet;
            } else {
                Set<Integer> parentItemTypeLimitations = null;
                Integer parentProjectID = childToParentProjectIDMap.get(projectID);
                String parentProjectLabel = null;
                if (parentProjectID != null) {
                    parentItemTypeLimitations = getItemTypeLimitations(parentProjectID, selectedItemTypeIDsSet,
                            projectIssueTypeMap, childToParentProjectIDMap);
                    if (LOGGER.isDebugEnabled()) {
                        parentProjectLabel = LookupContainer
                                .getNotLocalizedLabelBeanLabel(SystemFields.INTEGER_PROJECT, parentProjectID);
                    }
                    if (parentItemTypeLimitations != null && parentItemTypeLimitations.contains(null)) {
                        LOGGER.debug("The parent project " + parentProjectLabel
                                + " has role without item type limitation");
                        return parentItemTypeLimitations;
                    }
                }
                if (selectedItemTypeIDsSet == null || selectedItemTypeIDsSet.isEmpty()) {
                    LOGGER.debug(theProject + projectLabel
                            + " has role with item type limitation (no selected item type in filter)");
                    if (parentItemTypeLimitations != null) {
                        roleRestrictedItemTypeIDSet.addAll(parentItemTypeLimitations);
                        LOGGER.debug(theProject + projectLabel
                                + " inherits role with item type limitation (no selected item type in filter) from parent project "
                                + parentProjectLabel);
                    }
                    return roleRestrictedItemTypeIDSet;
                } else {
                    // clone the roleRestrictedItemTypeIDSet because retainAll
                    // modifies the set
                    Set<Integer> intersection = new HashSet<Integer>(roleRestrictedItemTypeIDSet);
                    LOGGER.debug(theProject + projectLabel
                            + " has role with item type limitation (with selected item type in filter)");
                    intersection.retainAll(selectedItemTypeIDsSet);
                    if (parentItemTypeLimitations != null) {
                        LOGGER.debug(theProject + projectLabel
                                + " inherits role with item type limitation (with selected item type in filter) from parent project "
                                + parentProjectLabel);
                        intersection.addAll(parentItemTypeLimitations);
                    }
                    return intersection;
                }
            }
        } else {
            LOGGER.debug(theProject + projectLabel + " has no direct role ");
            Integer parentProjectID = childToParentProjectIDMap.get(projectID);
            if (parentProjectID != null) {
                Set<Integer> parentItemTypeLimitations = getItemTypeLimitations(parentProjectID,
                        selectedItemTypeIDsSet, projectIssueTypeMap, childToParentProjectIDMap);
                if (parentItemTypeLimitations != null) {
                    String parentProjectLabel = null;
                    if (LOGGER.isDebugEnabled()) {
                        parentProjectLabel = LookupContainer
                                .getNotLocalizedLabelBeanLabel(SystemFields.INTEGER_PROJECT, parentProjectID);
                    }
                    if (parentItemTypeLimitations.contains(null)) {
                        LOGGER.debug("The parent project " + parentProjectLabel
                                + " has role without item type limitation");
                        return parentItemTypeLimitations;
                    } else {
                        LOGGER.debug(theProject + projectLabel
                                + " inherits role  with item type limitation from parent project "
                                + parentProjectLabel);
                        return parentItemTypeLimitations;
                    }
                }
            }
            return null;
        }
    }

    /**
     * Get the all project-issueTypes for a list of workitems
     *
     * @param workItemBeanList
     * @return
     */
    public static Map<Integer, Set<Integer>> getProjectToIssueTypesMap(Collection<TWorkItemBean> workItemBeanList) {
        Map<Integer, Set<Integer>> projectToIssueTypesMap = new HashMap<Integer, Set<Integer>>();
        if (workItemBeanList != null) {
            for (TWorkItemBean workItemBean : workItemBeanList) {
                Integer projectID = workItemBean.getProjectID();
                Integer issueTypeID = workItemBean.getListTypeID();
                Set<Integer> issueTypes = projectToIssueTypesMap.get(projectID);
                if (issueTypes == null) {
                    issueTypes = new HashSet<Integer>();
                    projectToIssueTypesMap.put(projectID, issueTypes);
                }
                issueTypes.add(issueTypeID);
            }
        }
        return projectToIssueTypesMap;
    }

    /**
     * Get the all project-issueTypes for a list of workitems
     *
     * @param reportBeanWithHistoryList
     * @return
     */
    private static Map<Integer, Set<Integer>> getProjectToIssueTypesMap(
            List<ReportBeanWithHistory> reportBeanWithHistoryList) {
        Map<Integer, Set<Integer>> projectToIssueTypesMap = new HashMap<Integer, Set<Integer>>();
        if (reportBeanWithHistoryList != null) {
            for (ReportBean reportBean : reportBeanWithHistoryList) {
                TWorkItemBean workItemBean = reportBean.getWorkItemBean();
                Integer projectID = workItemBean.getProjectID();
                Integer issueTypeID = workItemBean.getListTypeID();
                Set<Integer> issueTypes = projectToIssueTypesMap.get(projectID);
                if (issueTypes == null) {
                    issueTypes = new HashSet<Integer>();
                    projectToIssueTypesMap.put(projectID, issueTypes);
                }
                issueTypes.add(issueTypeID);
            }
        }
        return projectToIssueTypesMap;
    }

    /**
     * Filters out the cost beans from the reportBeanWithHistory objects when it
     * is not the person's own cost and she has no right to see the others'
     * costs
     *
     * @param personID
     * @param reportBeanWithHistoryList
     */
    public static void filterCostBeans(Integer personID, List<ReportBeanWithHistory> reportBeanWithHistoryList) {
        if (personID == null || reportBeanWithHistoryList == null || reportBeanWithHistoryList.isEmpty()) {
            return;
        }
        Map<Integer, Set<Integer>> projectToIssueTypesMap = getProjectToIssueTypesMap(reportBeanWithHistoryList);
        List<Integer> fieldIDs = new LinkedList<Integer>();
        fieldIDs.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES);
        Map<Integer, Map<Integer, Map<Integer, Integer>>> fieldRestrictions = getFieldRestrictions(personID,
                projectToIssueTypesMap, fieldIDs, false);
        for (ReportBeanWithHistory reportBean : reportBeanWithHistoryList) {
            TWorkItemBean workItemBean = reportBean.getWorkItemBean();
            List<TCostBean> costBeans = reportBean.getCosts();
            // get the category label
            if (costBeans != null && !costBeans.isEmpty()) {
                Integer projectID = workItemBean.getProjectID();
                Integer issueTypeID = workItemBean.getListTypeID();
                Map<Integer, Map<Integer, Integer>> issueTypeRestrictions = fieldRestrictions.get(projectID);
                Map<Integer, Integer> hiddenFields = null;
                if (issueTypeRestrictions != null) {
                    hiddenFields = issueTypeRestrictions.get(issueTypeID);
                }
                if (hiddenFields != null
                        && hiddenFields.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES)) {
                    Iterator<TCostBean> iterator = costBeans.iterator();
                    while (iterator.hasNext()) {
                        TCostBean costBean = iterator.next();
                        if (!personID.equals(costBean.getChangedByID())) {
                            iterator.remove();
                        }
                    }
                }
            }
        }
    }

    /**
     * Filters out the cost beans from the reportBeanWithHistory objects when it
     * is not the person's own cost and he/she has no right to see the others'
     * costs
     *
     * @param costBeans
     * @param personID
     * @param workItemBeansMap
     */
    public static void filterCostBeans(List<TCostBean> costBeans, Integer personID,
            Map<Integer, TWorkItemBean> workItemBeansMap) {
        if (personID == null || costBeans == null || costBeans.isEmpty()) {
            return;
        }
        Map<Integer, Set<Integer>> projectToIssueTypesMap = getProjectToIssueTypesMap(workItemBeansMap.values());
        List<Integer> fieldIDs = new LinkedList<Integer>();
        fieldIDs.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES);
        Map<Integer, Map<Integer, Map<Integer, Integer>>> fieldRestrictions = getFieldRestrictions(personID,
                projectToIssueTypesMap, fieldIDs, false);
        for (Iterator<TCostBean> itrCostBeans = costBeans.iterator(); itrCostBeans.hasNext();) {
            TCostBean costBean = itrCostBeans.next();
            Integer changedBy = costBean.getChangedByID();
            if (changedBy != null && !changedBy.equals(personID)) {
                // my expenses are always visible
                Integer workItemID = costBean.getWorkItemID();
                TWorkItemBean workItemBean = workItemBeansMap.get(workItemID);
                Integer projectID = workItemBean.getProjectID();
                Integer issueTypeID = workItemBean.getListTypeID();
                Map<Integer, Map<Integer, Integer>> issueTypeRestrictions = fieldRestrictions.get(projectID);
                Map<Integer, Integer> hiddenFields = null;
                if (issueTypeRestrictions != null) {
                    hiddenFields = issueTypeRestrictions.get(issueTypeID);
                }
                if (hiddenFields != null
                        && hiddenFields.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ALL_EXPENSES)) {
                    itrCostBeans.remove();
                }
            }
        }
    }

    /**
     * Filters out the cost beans from the reportBeanWithHistory objects when it
     * is not the person's own cost and he/she has no right to see the others'
     * costs
     *
     * @param budgetBeans
     * @param personID
     * @param workItemBeansMap
     */
    public static void filterBudgetBeans(List<TBudgetBean> budgetBeans, Integer personID,
            Map<Integer, TWorkItemBean> workItemBeansMap) {
        if (personID == null || budgetBeans == null || budgetBeans.isEmpty()) {
            return;
        }
        Map<Integer, Set<Integer>> projectToIssueTypesMap = getProjectToIssueTypesMap(workItemBeansMap.values());
        List<Integer> fieldIDs = new LinkedList<Integer>();
        fieldIDs.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.PLAN);
        fieldIDs.add(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.BUDGET);
        Map<Integer, Map<Integer, Map<Integer, Integer>>> fieldRestrictions = getFieldRestrictions(personID,
                projectToIssueTypesMap, fieldIDs, false);
        for (Iterator<TBudgetBean> iterator = budgetBeans.iterator(); iterator.hasNext();) {
            TBudgetBean budgetBean = iterator.next();
            Integer changedBy = budgetBean.getChangedByID();
            if (changedBy != null && !changedBy.equals(personID)) {
                Integer budgetType = budgetBean.getBudgetType();
                Integer workItemID = budgetBean.getWorkItemID();
                TWorkItemBean workItemBean = workItemBeansMap.get(workItemID);
                Integer projectID = workItemBean.getProjectID();
                Integer issueTypeID = workItemBean.getListTypeID();
                Map<Integer, Map<Integer, Integer>> issueTypeRestrictions = fieldRestrictions.get(projectID);
                Map<Integer, Integer> hiddenFields = null;
                if (issueTypeRestrictions != null) {
                    hiddenFields = issueTypeRestrictions.get(issueTypeID);
                }
                if (hiddenFields != null) {
                    if ((budgetType == null || TBudgetBean.BUDGET_TYPE.PLANNED_VALUE.equals(budgetType))
                            && hiddenFields.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.PLAN)) {
                        iterator.remove();
                    } else {
                        if ((TBudgetBean.BUDGET_TYPE.BUDGET.equals(budgetType))
                                && hiddenFields.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.BUDGET)) {
                            iterator.remove();
                        }
                    }
                }
            }
        }
    }

    public static final int NUMBER_OF_ACCESS_FLAGS = 11;

    /**
     * The positional constants from the extendedAccessKey for permission
     * definitions. Free positions between defined positions are intentional.
     *
     */
    public static class AccessFlagIndexes {

        private AccessFlagIndexes() {
        }

        /**
         * Permission to read any issue.
         */
        public static final int READANYTASK = 0;

        /**
         * Permission to modify any issue.
         */
        public static final int MODIFYANYTASK = 1;

        /**
         * Permission to create issues.
         */
        public static final int CREATETASK = 2;

        /**
         * Permission to close any task.
         */
        public static final int CLOSEANYTASK = 3;

        /**
         * Permission to close a task if the user is the current responsible.
         */
        public static final int CLOSETASKIFRESPONSIBLE = 4;

        /**
         * Permission to close a task if the user is the current manager or
         * original author.
         */
        public static final int CLOSETASKIFMANAGERORORIGINATOR = 5;

        /**
         * Not really a permission, but rather a flag to include this user in
         * the selection box for managers.
         */
        public static final int MANAGER = 6;

        /**
         * Not really a permission, but rather a flag to include this user in
         * the selection box for responsibles.
         */
        public static final int RESPONSIBLE = 7;

        /**
         * Not really a permission, but rather a flag to include this user in
         * the selection box for consulted.
         */
        public static final int CONSULTANT = 8;

        /**
         * Not really a permission, but rather a flag to include this user in
         * the selection box for informed.
         */
        public static final int INFORMANT = 9;

        /**
         * This user has project administrator rights, which comprise a whole
         * lot of specific permissions like being able to assign roles to users
         * in this project, and so on.
         */
        public static final int PROJECTADMIN = 10;

        /**
         * Permission to add, modify, and delete budgets (planned values).
         *
         * @deprecated
         */
        @Deprecated
        public static final int ADDMODIFYDELETEBUDGET = 12;

        /**
         * Permission to add, modify, and delete due dates.
         *
         * @deprecated
         */
        @Deprecated
        public static final int ADDMODIFYDELETEDUEDATES = 13;

        /**
         * Permission to modify the users own work and cost entries.
         *
         * @deprecated
         */
        @Deprecated
        public final static int ADDMODIFYDELETEOWNHOURSCOST = 14;

        /**
         * Permission to view all work and cost entries, not just the users own.
         *
         * @deprecated
         */
        @Deprecated
        public static final int VIEWALLHOURSCOST = 15;

        /**
         * Permission to assign a task to a new responsible or manager.
         *
         * @deprecated
         */
        @Deprecated
        public static final int ASSIGNTASKTORESPONSIBLEORMANAGER = 16;

        /**
         * @deprecated
         */
        @Deprecated
        public static final int MODIFYCONSULTANTSINFORMANTANDOTHERWATCHERS = 17;

        /**
         * Permission to modify the list of consulted and informed.
         *
         * @deprecated
         */
        public static final int VIEWCONSULTANTSINFORMANTANDOTHERWATCHERS = 18;

        /**
         * Permission to modify the issue title (synopsis) and description.
         * @deprecated
         */
        @Deprecated
        public static final int MODIFYSYNOPSISORDESCRIPTION = 20;

    }
}