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

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.CoreConstants;
import org.kuali.rice.core.api.config.ConfigurationException;
import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
import org.kuali.rice.core.api.exception.RiceIllegalStateException;
import org.kuali.rice.core.api.membership.MemberType;
import org.kuali.rice.core.api.util.VersionHelper;
import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
import org.kuali.rice.kew.actionrequest.ActionRequestValue;
import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
import org.kuali.rice.kew.actionrequest.Recipient;
import org.kuali.rice.kew.api.action.ActionRequestPolicy;
import org.kuali.rice.kew.api.action.ActionRequestType;
import org.kuali.rice.kew.api.action.RecipientType;
import org.kuali.rice.kew.api.document.Document;
import org.kuali.rice.kew.api.document.DocumentContent;
import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
import org.kuali.rice.kew.api.peopleflow.PeopleFlowDelegate;
import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
import org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService;
import org.kuali.rice.kew.engine.RouteContext;
import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService;
import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
import org.kuali.rice.kim.api.group.Group;
import org.kuali.rice.kim.api.identity.principal.Principal;
import org.kuali.rice.kim.api.role.Role;
import org.kuali.rice.kim.api.role.RoleMembership;
import org.kuali.rice.kim.api.role.RoleService;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.ksb.api.KsbApiServiceLocator;
import org.kuali.rice.ksb.api.bus.Endpoint;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Reference implementation of the {@code PeopleFlowRequestGenerator} which is responsible for generating Action
 * Requests from a {@link PeopleFlowDefinition}.
 *
 * @author Kuali Rice Team (<EMAIL>)
 */
public class PeopleFlowRequestGeneratorImpl implements PeopleFlowRequestGenerator {

    private KewTypeRepositoryService typeRepositoryService;
    private RoleService roleService;

    @Override
    public List<ActionRequestValue> generateRequests(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
        Context context = new Context(routeContext, peopleFlow, actionRequested);
        for (PeopleFlowMember member : peopleFlow.getMembers()) {
            generateRequestForMember(context, member);
        }

        return context.getActionRequestFactory().getRequestGraphs();
    }

    protected void generateRequestForMember(Context context, PeopleFlowMember member) {
        // used later for generating any delegate requests
        List<ActionRequestValue> memberRequests = new ArrayList<ActionRequestValue>();

        if (MemberType.ROLE == member.getMemberType()) {
            memberRequests.addAll(findNonRoleRequests(generateRequestsForRoleMember(context, member)));
        } else {
            ActionRequestValue actionRequest = context.getActionRequestFactory().addRootActionRequest(
                    context.getActionRequested().getCode(), member.getPriority(), toRecipient(member), "",
                    member.getResponsibilityId(), member.getForceAction(), getActionRequestPolicyCode(member), null);

            if (actionRequest != null) {
                memberRequests.add(actionRequest);
            }
        }

        // KULRICE-5726: Add support for delegates on roles in PeopleFlows as well as using roles as delegates
        generateDelegationRequests(context, memberRequests, member);
    }

    /**
     * generates requests for a PeopleFlow member of type Role.
     *
     * <p>Will resolve role qualifiers through the appropriate PeopleFlowTypeService, and if multiple qualifier maps are
     * generated, it can generate multiple request trees.
     * parent requests</p>
     *
     * @param context the context for request generation
     * @param member the PeopleFlow member, which is assumed to be of MemberType ROLE
     * @return a list of root action requests that were generated, or an empty list if no role members were resolved
     */
    protected List<ActionRequestValue> generateRequestsForRoleMember(Context context, PeopleFlowMember member) {
        List<ActionRequestValue> roleMemberRequests = new ArrayList<ActionRequestValue>(); // results

        List<Map<String, String>> roleQualifierMaps = loadRoleQualifiers(context, member.getMemberId());
        Role role = getRoleService().getRole(member.getMemberId());

        boolean hasPeopleFlowDelegates = !CollectionUtils.isEmpty(member.getDelegates());

        if (role == null) {
            throw new IllegalStateException("Failed to locate a role with the given role id of '" +
                    member.getMemberId() + "'");
        }

        if (CollectionUtils.isEmpty(roleQualifierMaps)) {
            ActionRequestValue request = addKimRoleRequest(context, member, role, Collections.<String, String>emptyMap(),
                    hasPeopleFlowDelegates);

            if (request != null) {
                roleMemberRequests.add(request);

                if (hasPeopleFlowDelegates) {
                    generateDelegationToRoleRequests(context, request, member, Collections.<String, String>emptyMap());
                }
            }
        } else {
            // we may have multiple maps of role qualifiers, so we'll add a request for each map
            for (Map<String, String> roleQualifiers : roleQualifierMaps) {
                ActionRequestValue request = addKimRoleRequest(context, member, role, roleQualifiers,
                        hasPeopleFlowDelegates);

                if (request != null) {
                    roleMemberRequests.add(request);

                    if (hasPeopleFlowDelegates) {
                        generateDelegationToRoleRequests(context, request, member, roleQualifiers);
                    }
                }
            }
        }

        return roleMemberRequests;
    }

    /**
     * Uses the ActionRequestFactory to build the request tree for the role members.
     *
     * <p>The role members themselves are derived here using the given qualifiers.</p>
     *
     * @param context the context for request generation
     * @param member the PeopleFlow member
     * @param role the role specified within the member
     * @param roleQualifiers the qualifiers to use for role member selection
     * @param ignoreKimDelegates should KIM delegates be ignored when generating requests?
     * @return the root request of the generated action request tree, or null if no members are found
     */
    private ActionRequestValue addKimRoleRequest(Context context, PeopleFlowMember member, Role role,
            Map<String, String> roleQualifiers, boolean ignoreKimDelegates) {

        ActionRequestValue roleMemberRequest = null;

        List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
                member.getMemberId()), roleQualifiers);

        String actionRequestPolicyCode = getActionRequestPolicyCode(member);

        if (!CollectionUtils.isEmpty(memberships)) {
            roleMemberRequest = context.getActionRequestFactory().addKimRoleRequest(
                    context.getActionRequested().getCode(), member.getPriority(), role, memberships, null,
                    member.getResponsibilityId(), member.getForceAction(), actionRequestPolicyCode, null, ignoreKimDelegates);
        }

        return roleMemberRequest;
    }

    /**
     * Generates any needed requests for {@link PeopleFlowDelegate}s on the given member.
     *
     * <p>If there are no delegates, or if no requests were generated for the member, then this will be a no-op.</p>
     *
     * @param context the context for request generation
     * @param memberRequests any action requests that were generated for the given member
     * @param member the PeopleFlow member
     */
    private void generateDelegationRequests(Context context, List<ActionRequestValue> memberRequests,
            PeopleFlowMember member) {

        if (CollectionUtils.isEmpty(member.getDelegates()) || CollectionUtils.isEmpty(memberRequests)) {
            return;
        }

        for (PeopleFlowDelegate delegate : member.getDelegates()) {
            for (ActionRequestValue memberRequest : memberRequests) {

                // role delegations already created in generateRequestsForRoleMember
                // in order to include member role qualifiers
                if (MemberType.ROLE != delegate.getMemberType()) {
                    generateDelegationToNonRoleRequest(context, memberRequest, member, delegate);
                }
            }
        }
    }

    /**
     * Uses the ActionRequestFactory to add the delegate request to the given parent request.
     *
     * <p>Only handles non-role delegates.  If a delegate of type role is passed, a RiceIllegalStateException will be
     * thrown.</p>
     *
     * @param context the context for request generation
     * @param memberRequest an action request that was generated for the given member
     * @param member the PeopleFlow member
     * @param delegate the delegate to generate a request to
     */
    private void generateDelegationToNonRoleRequest(Context context, ActionRequestValue memberRequest,
            PeopleFlowMember member, PeopleFlowDelegate delegate) {

        Recipient recipient;

        if (MemberType.PRINCIPAL == delegate.getMemberType()) {
            recipient = new KimPrincipalRecipient(delegate.getMemberId());
        } else if (MemberType.GROUP == delegate.getMemberType()) {
            recipient = new KimGroupRecipient(delegate.getMemberId());
        } else {
            throw new RiceIllegalStateException("MemberType unknown: " + delegate.getMemberType());
        }

        String delegationAnnotation = generateDelegationAnnotation(memberRequest, member, delegate);

        context.getActionRequestFactory().addDelegationRequest(memberRequest, recipient,
                delegate.getResponsibilityId(), member.getForceAction(),
                delegate.getDelegationType(), delegationAnnotation, null);
    }

    /**
     * Builds the String that will be used for the annotation on the delegate requests
     *
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member
     * @param delegate the delegate
     * @return the annotation string
     */
    private String generateDelegationAnnotation(ActionRequestValue parentRequest, PeopleFlowMember member,
            PeopleFlowDelegate delegate) {

        StringBuffer annotation = new StringBuffer( "Delegation of: " );
        annotation.append( parentRequest.getAnnotation() );
        annotation.append( " to " );

        if (delegate.getMemberType() == MemberType.PRINCIPAL) {
            annotation.append( "principal " );
            Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(delegate.getMemberId());

            if ( principal != null ) {
                annotation.append( principal.getPrincipalName() );
            } else {
                annotation.append( member.getMemberId() );
            }
        } else if (delegate.getMemberType() == MemberType.GROUP) {
            annotation.append( "group " );
            Group group = KimApiServiceLocator.getGroupService().getGroup(delegate.getMemberId());

            if ( group != null ) {
                annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() );
            } else {
                annotation.append( member.getMemberId() );
            }
        } else {
            annotation.append( "?????? '" );
            annotation.append( member.getMemberId() );
            annotation.append( "'" );
        }

        return annotation.toString();
    }


    /**
     * Generates any needed requests for the given {@link PeopleFlowDelegate}.
     *
     * <p>It is assumed that the given member has the given delegate configured.</p>
     *
     * @param context the context for request generation
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member, which should contain the given delegate
     * @param roleQualifier member's qualifier
     */
    protected void generateDelegationToRoleRequests(Context context, ActionRequestValue parentRequest,
            PeopleFlowMember member, Map<String, String> roleQualifier) {

        for (PeopleFlowDelegate delegate : member.getDelegates()) {

            if (MemberType.ROLE.equals(delegate.getMemberType())) {
                Role delegateRole = getRoleService().getRole(delegate.getMemberId());

                if (delegateRole != null) {
                    addKimRoleDelegateRequest(context, parentRequest, member, delegate, delegateRole,
                            roleQualifier);
                }
            }
        }
    }

    /**
     * Helper method uses the ActionRequestFactory to add to the parent request the delegation request(s) to a role.
     *
     * <p>The role members themselves are derived here using the given qualifiers.</p>
     *
     * @param context the context for request generation
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member
     * @param delegate the delegate to generate a request to
     * @param role the role specified within the delegate
     * @param roleQualifiers the qualifiers to use for role member selection
     */
    private void addKimRoleDelegateRequest(Context context, ActionRequestValue parentRequest,
            PeopleFlowMember member, PeopleFlowDelegate delegate, Role role, Map<String, String> roleQualifiers) {

        // sanity check
        if (delegate.getMemberType() != MemberType.ROLE) {
            throw new RiceIllegalArgumentException("delegate's member type must be ROLE");
        } else if (!delegate.getMemberId().equals(role.getId())) {
            throw new RiceIllegalArgumentException("delegate's member id must match the given role's id");
        }
<commit_msg>[KULRICE-13973] IT Failure PeopleFlowRoutingTest <commit_after>/**
 * Copyright 2005-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.kew.impl.peopleflow;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.CoreConstants;
import org.kuali.rice.core.api.config.ConfigurationException;
import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
import org.kuali.rice.core.api.exception.RiceIllegalStateException;
import org.kuali.rice.core.api.membership.MemberType;
import org.kuali.rice.core.api.util.VersionHelper;
import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
import org.kuali.rice.kew.actionrequest.ActionRequestValue;
import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
import org.kuali.rice.kew.actionrequest.Recipient;
import org.kuali.rice.kew.api.action.ActionRequestPolicy;
import org.kuali.rice.kew.api.action.ActionRequestType;
import org.kuali.rice.kew.api.action.RecipientType;
import org.kuali.rice.kew.api.document.Document;
import org.kuali.rice.kew.api.document.DocumentContent;
import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
import org.kuali.rice.kew.api.peopleflow.PeopleFlowDelegate;
import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
import org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService;
import org.kuali.rice.kew.engine.RouteContext;
import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService;
import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
import org.kuali.rice.kim.api.group.Group;
import org.kuali.rice.kim.api.identity.principal.Principal;
import org.kuali.rice.kim.api.role.Role;
import org.kuali.rice.kim.api.role.RoleMembership;
import org.kuali.rice.kim.api.role.RoleService;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.ksb.api.KsbApiServiceLocator;
import org.kuali.rice.ksb.api.bus.Endpoint;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Reference implementation of the {@code PeopleFlowRequestGenerator} which is responsible for generating Action
 * Requests from a {@link PeopleFlowDefinition}.
 *
 * @author Kuali Rice Team (<EMAIL>)
 */
public class PeopleFlowRequestGeneratorImpl implements PeopleFlowRequestGenerator {

    private KewTypeRepositoryService typeRepositoryService;
    private RoleService roleService;

    @Override
    public List<ActionRequestValue> generateRequests(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
        Context context = new Context(routeContext, peopleFlow, actionRequested);
        for (PeopleFlowMember member : peopleFlow.getMembers()) {
            generateRequestForMember(context, member);
        }

        return context.getActionRequestFactory().getRequestGraphs();
    }

    protected void generateRequestForMember(Context context, PeopleFlowMember member) {
        // used later for generating any delegate requests
        List<ActionRequestValue> memberRequests = new ArrayList<ActionRequestValue>();

        if (MemberType.ROLE == member.getMemberType()) {
            memberRequests.addAll(findNonRoleRequests(generateRequestsForRoleMember(context, member)));
        } else {
            ActionRequestValue actionRequest = context.getActionRequestFactory().addRootActionRequest(
                    context.getActionRequested().getCode(), member.getPriority(), toRecipient(member), "",
                    member.getResponsibilityId(), member.getForceAction(), getActionRequestPolicyCode(member), null);

            if (actionRequest != null) {
                memberRequests.add(actionRequest);
            }
        }

        // KULRICE-5726: Add support for delegates on roles in PeopleFlows as well as using roles as delegates
        generateDelegationRequests(context, memberRequests, member);
    }

    /**
     * generates requests for a PeopleFlow member of type Role.
     *
     * <p>Will resolve role qualifiers through the appropriate PeopleFlowTypeService, and if multiple qualifier maps are
     * generated, it can generate multiple request trees.
     * parent requests</p>
     *
     * @param context the context for request generation
     * @param member the PeopleFlow member, which is assumed to be of MemberType ROLE
     * @return a list of root action requests that were generated, or an empty list if no role members were resolved
     */
    protected List<ActionRequestValue> generateRequestsForRoleMember(Context context, PeopleFlowMember member) {
        List<ActionRequestValue> roleMemberRequests = new ArrayList<ActionRequestValue>(); // results

        List<Map<String, String>> roleQualifierMaps = loadRoleQualifiers(context, member.getMemberId());
        Role role = getRoleService().getRole(member.getMemberId());

        boolean hasPeopleFlowDelegates = !CollectionUtils.isEmpty(member.getDelegates());

        if (role == null) {
            throw new IllegalStateException("Failed to locate a role with the given role id of '" +
                    member.getMemberId() + "'");
        }

        if (CollectionUtils.isEmpty(roleQualifierMaps)) {
            ActionRequestValue request = addKimRoleRequest(context, member, role, Collections.<String, String>emptyMap(),
                    hasPeopleFlowDelegates);

            if (request != null) {
                roleMemberRequests.add(request);

                if (hasPeopleFlowDelegates) {
                    generateDelegationRequestsForRoleMember(context, request, member, Collections.<String, String>emptyMap());
                }
            }
        } else {
            // we may have multiple maps of role qualifiers, so we'll add a request for each map
            for (Map<String, String> roleQualifiers : roleQualifierMaps) {
                ActionRequestValue request = addKimRoleRequest(context, member, role, roleQualifiers,
                        hasPeopleFlowDelegates);

                if (request != null) {
                    roleMemberRequests.add(request);

                    if (hasPeopleFlowDelegates) {
                        generateDelegationRequestsForRoleMember(context, request, member, roleQualifiers);
                    }
                }
            }
        }

        return roleMemberRequests;
    }

    /**
     * Uses the ActionRequestFactory to build the request tree for the role members.
     *
     * <p>The role members themselves are derived here using the given qualifiers.</p>
     *
     * @param context the context for request generation
     * @param member the PeopleFlow member
     * @param role the role specified within the member
     * @param roleQualifiers the qualifiers to use for role member selection
     * @param ignoreKimDelegates should KIM delegates be ignored when generating requests?
     * @return the root request of the generated action request tree, or null if no members are found
     */
    private ActionRequestValue addKimRoleRequest(Context context, PeopleFlowMember member, Role role,
            Map<String, String> roleQualifiers, boolean ignoreKimDelegates) {

        ActionRequestValue roleMemberRequest = null;

        List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
                member.getMemberId()), roleQualifiers);

        String actionRequestPolicyCode = getActionRequestPolicyCode(member);

        if (!CollectionUtils.isEmpty(memberships)) {
            roleMemberRequest = context.getActionRequestFactory().addKimRoleRequest(
                    context.getActionRequested().getCode(), member.getPriority(), role, memberships, null,
                    member.getResponsibilityId(), member.getForceAction(), actionRequestPolicyCode, null, ignoreKimDelegates);
        }

        return roleMemberRequest;
    }

    /**
     * Generates any needed requests for {@link PeopleFlowDelegate}s on the given member.
     *
     * <p>If there are no delegates, or if no requests were generated for the member, then this will be a no-op.</p>
     *
     * @param context the context for request generation
     * @param memberRequests any action requests that were generated for the given member
     * @param member the PeopleFlow member
     */
    private void generateDelegationRequests(Context context, List<ActionRequestValue> memberRequests,
            PeopleFlowMember member) {

        if (CollectionUtils.isEmpty(member.getDelegates()) || CollectionUtils.isEmpty(memberRequests)) {
            return;
        }

        for (PeopleFlowDelegate delegate : member.getDelegates()) {
            for (ActionRequestValue memberRequest : memberRequests) {
                if (MemberType.ROLE != delegate.getMemberType()) {
                    generateDelegationToNonRoleRequest(context, memberRequest, member, delegate);
                } else {
                    generateDelegationToRoleRequests(context, memberRequest, member, delegate);
                }
            }
        }
    }

    /**
     * Uses the ActionRequestFactory to add the delegate request to the given parent request.
     *
     * <p>Only handles non-role delegates.  If a delegate of type role is passed, a RiceIllegalStateException will be
     * thrown.</p>
     *
     * @param context the context for request generation
     * @param memberRequest an action request that was generated for the given member
     * @param member the PeopleFlow member
     * @param delegate the delegate to generate a request to
     */
    private void generateDelegationToNonRoleRequest(Context context, ActionRequestValue memberRequest,
            PeopleFlowMember member, PeopleFlowDelegate delegate) {

        Recipient recipient;

        if (MemberType.PRINCIPAL == delegate.getMemberType()) {
            recipient = new KimPrincipalRecipient(delegate.getMemberId());
        } else if (MemberType.GROUP == delegate.getMemberType()) {
            recipient = new KimGroupRecipient(delegate.getMemberId());
        } else {
            throw new RiceIllegalStateException("MemberType unknown: " + delegate.getMemberType());
        }

        String delegationAnnotation = generateDelegationAnnotation(memberRequest, member, delegate);

        context.getActionRequestFactory().addDelegationRequest(memberRequest, recipient,
                delegate.getResponsibilityId(), member.getForceAction(),
                delegate.getDelegationType(), delegationAnnotation, null);
    }

    /**
     * Generates any needed requests for the given {@link PeopleFlowDelegate}.
     *
     * <p>It is assumed that the given member is a Role.</p>
     *
     * @param context the context for request generation
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member, which should contain the given delegate
     * @param roleQualifier member's qualifier
     */
    protected void generateDelegationRequestsForRoleMember(Context context, ActionRequestValue parentRequest,
            PeopleFlowMember member, Map<String, String> roleQualifier) {

        for (PeopleFlowDelegate delegate : member.getDelegates()) {
            if (MemberType.ROLE.equals(delegate.getMemberType())) {
                Role delegateRole = getRoleService().getRole(delegate.getMemberId());

                if (delegateRole != null) {
                    addKimRoleDelegateRequest(context, parentRequest, member, delegate, delegateRole,
                            roleQualifier);
                }
            }
        }
    }

    /**
     * Builds the String that will be used for the annotation on the delegate requests
     *
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member
     * @param delegate the delegate
     * @return the annotation string
     */
    private String generateDelegationAnnotation(ActionRequestValue parentRequest, PeopleFlowMember member,
            PeopleFlowDelegate delegate) {

        StringBuffer annotation = new StringBuffer( "Delegation of: " );
        annotation.append( parentRequest.getAnnotation() );
        annotation.append( " to " );

        if (delegate.getMemberType() == MemberType.PRINCIPAL) {
            annotation.append( "principal " );
            Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(delegate.getMemberId());

            if ( principal != null ) {
                annotation.append( principal.getPrincipalName() );
            } else {
                annotation.append( member.getMemberId() );
            }
        } else if (delegate.getMemberType() == MemberType.GROUP) {
            annotation.append( "group " );
            Group group = KimApiServiceLocator.getGroupService().getGroup(delegate.getMemberId());

            if ( group != null ) {
                annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() );
            } else {
                annotation.append( member.getMemberId() );
            }
        } else {
            annotation.append( "?????? '" );
            annotation.append( member.getMemberId() );
            annotation.append( "'" );
        }

        return annotation.toString();
    }

    /**
     * Generates any needed requests for the given {@link PeopleFlowDelegate}.
     *
     * <p>It is assumed that the given member has the given delegate configured.</p>
     *
     * @param context the context for request generation
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member, which should contain the given delegate
     * @param delegate the delegate, assumed to be of MemberType ROLE, to generate a request to
     */
    protected void generateDelegationToRoleRequests(Context context,
            ActionRequestValue parentRequest, PeopleFlowMember member, PeopleFlowDelegate delegate) {

        List<Map<String, String>> roleQualifierList = loadRoleQualifiers(context, delegate.getMemberId());
        Role role = getRoleService().getRole(delegate.getMemberId());

        if (role == null) {
            throw new IllegalStateException("Failed to locate a role with the given role id of '" +
                    delegate.getMemberId() + "'");
        }

        if (CollectionUtils.isEmpty(roleQualifierList)) {
            addKimRoleDelegateRequest(context, parentRequest, member, delegate, role,
                    Collections.<String, String>emptyMap());
        } else {
            for (Map<String, String> roleQualifiers : roleQualifierList) {
                addKimRoleDelegateRequest(context, parentRequest, member, delegate, role, roleQualifiers);
            }
        }
    }

    /**
     * Helper method uses the ActionRequestFactory to add to the parent request the delegation request(s) to a role.
     *
     * <p>The role members themselves are derived here using the given qualifiers.</p>
     *
     * @param context the context for request generation
     * @param parentRequest an action request that was generated for the given member
     * @param member the PeopleFlow member
     * @param delegate the delegate to generate a request to
     * @param role the role specified within the delegate
     * @param roleQualifiers the qualifiers to use for role member selection
     */
    private void addKimRoleDelegateRequest(Context context, ActionRequestValue parentRequest,
            PeopleFlowMember member, PeopleFlowDelegate delegate, Role role, Map<String, String> roleQualifiers) {

        // sanity check
        if (delegate.getMemberType() != MemberType.ROLE) {
            throw new RiceIllegalArgumentException("delegate's member type must be ROLE");
        } else if (!delegate.getMemberId().equals(role.getId())) {
            throw new RiceIllegalArgumentException("delegate's member id must match the given role's id");
        }

{'fraction_non_alphanumeric': 0.08359748896761762, 'fraction_numerical': 0.0010876996705823855, 'mean_word_length': 3.0400502197112367, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2684583', 'n_tokens_mistral': 8213, 'n_tokens_neox': 7943, 'n_words': 2272}