/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.coa.identity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.coa.businessobject.Organization;
import org.kuali.kfs.coa.service.ChartService;
import org.kuali.kfs.coa.service.OrganizationService;
import org.kuali.kfs.sys.identity.KfsKimAttributes;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.common.delegate.DelegateMember;
import org.kuali.rice.kim.api.role.RoleMembership;
import org.kuali.rice.kim.api.role.RoleMembership.Builder;
import org.kuali.rice.kim.framework.common.delegate.DelegationTypeService;
import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
import org.kuali.rice.kns.kim.role.RoleTypeServiceBase;
public abstract class OrganizationHierarchyAwareRoleTypeServiceBase extends RoleTypeServiceBase implements DelegationTypeService {
private static final Logger LOG = Logger.getLogger(OrganizationHierarchyAwareRoleTypeServiceBase.class);
protected ChartService chartService;
protected OrganizationService organizationService;
protected static final String DOCUMENT_TYPE_NAME = "ORR";
private List<String> workflowRoutingAttributes = new ArrayList<String>(2);
{
workflowRoutingAttributes.add( KfsKimAttributes.CHART_OF_ACCOUNTS_CODE );
workflowRoutingAttributes.add( KfsKimAttributes.ORGANIZATION_CODE );
workflowRoutingAttributes = Collections.unmodifiableList(workflowRoutingAttributes);
}
private List<String> uniqueAttributes = new ArrayList<String>(3);
{
uniqueAttributes.add(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME);
uniqueAttributes.add(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE);
uniqueAttributes.add(KfsKimAttributes.ORGANIZATION_CODE);
uniqueAttributes = Collections.unmodifiableList(uniqueAttributes);
}
@Override
public List<String> getUniqueAttributes(String kimTypeId){
return uniqueAttributes;
}
@Override
public List<String> getWorkflowRoutingAttributes(String routeLevel) {
return workflowRoutingAttributes;
}
@Override
public String getWorkflowDocumentTypeName() {
return DOCUMENT_TYPE_NAME;
}
protected boolean isParentOrg(String qualificationChartCode, String qualificationOrgCode, String roleChartCode, String roleOrgCode, boolean descendHierarchy) {
if ( StringUtils.isBlank(qualificationChartCode) || StringUtils.isBlank(qualificationOrgCode) ) {
if ( LOG.isInfoEnabled() ) {
LOG.info("No chart/org qualifications passed into isParentOrg()");
}
return false;
}
if (roleChartCode == null && roleOrgCode == null) {
LOG.warn("Call to "+this.getClass().getName()+" with no organization role qualifiers; both chart and organization code are null. Please ensure that qualification data has organization information for this role.");
return false;
}
if (roleOrgCode == null) {
return roleChartCode.equals(qualificationChartCode)
|| (descendHierarchy && chartService.isParentChart(qualificationChartCode, roleChartCode));
}
return (roleChartCode.equals(qualificationChartCode) && roleOrgCode.equals(qualificationOrgCode))
|| (descendHierarchy && organizationService.isParentOrganization(qualificationChartCode, qualificationOrgCode, roleChartCode, roleOrgCode));
}
@Override
public Map<String,String> convertQualificationForMemberRoles(String namespaceCode, String roleName, String memberRoleNamespaceCode, String memberRoleName, Map<String,String> qualification) {
if ( qualification == null ) {
return null;
}
// only attempt the conversion if :
// (a) there is not already a campus code provided by the document
// and (b) we have a chart and organization to resolve
if ( StringUtils.isBlank( qualification.get(KimConstants.AttributeConstants.CAMPUS_CODE ) )
&& StringUtils.isNotBlank( KfsKimAttributes.CHART_OF_ACCOUNTS_CODE)
&& StringUtils.isNotBlank( KfsKimAttributes.ORGANIZATION_CODE ) ) {
Map<String,String> newQualification = new HashMap<String,String>(qualification);
try {
Organization org = organizationService.getByPrimaryId(qualification.get(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE), qualification.get(KfsKimAttributes.ORGANIZATION_CODE));
if ( org != null ) {
newQualification.put(KimConstants.AttributeConstants.CAMPUS_CODE, org.getOrganizationPhysicalCampusCode());
} else {
if ( LOG.isDebugEnabled() ) {
LOG.debug( "Invalid Chart/Org passed to convertQualificationForMemberRoles: " + namespaceCode + "/" + roleName + "/" + memberRoleNamespaceCode + "/" + memberRoleName + "/" + qualification );
}
}
} catch (Exception ex) {
LOG.warn("Unable to convert organization qualification to physical campus", ex);
}
return newQualification;
} else {
return qualification;
}
}
@Override
public boolean doesDelegationQualifierMatchQualification(Map<String,String> qualification, Map<String,String> delegationQualifier) {
return performMatch(translateInputAttributes(qualification), delegationQualifier);
}
public List<DelegateMember> doDelegationQualifiersMatchQualification(Map<String,String> qualification, List<DelegateMemberBo> delegationMemberList) {
Map<String,String> translatedQualification = translateInputAttributes(qualification);
List<DelegateMember> matchingMemberships = new ArrayList<DelegateMember>();
for (DelegateMemberBo dmi : delegationMemberList) {
if (performMatch(translatedQualification, dmi.getQualifier())) {
matchingMemberships.add(DelegateMemberBo.to(dmi));
}
}
return matchingMemberships;
}
public void setOrganizationService(OrganizationService organizationService) {
this.organizationService = organizationService;
}
public void setChartService(ChartService chartService) {
this.chartService = chartService;
}
@Override
public List<RoleMembership> sortRoleMembers(List<RoleMembership> roleMembers) {
List<SortableRoleMembershipHolder> listToSort = new ArrayList<SortableRoleMembershipHolder>( roleMembers.size() );
// build the sortable list
for ( RoleMembership rmi : roleMembers ) {
listToSort.add( new SortableRoleMembershipHolder( rmi ) );
}
// sort it
Collections.sort(listToSort);
// restore them to the list in their sorted order
List<RoleMembership> newRoleMembers = new ArrayList<RoleMembership>(); // need a new list, because roleMembers is not mutable
int group = 0; // counter for the group number to add to the roleSortingCode
String lastRoleSortingCode = "";
for ( SortableRoleMembershipHolder srmh : listToSort ) {
if ( !srmh.rmi.getRoleSortingCode().equals( lastRoleSortingCode ) ) {
group++;
lastRoleSortingCode = srmh.rmi.getRoleSortingCode();
}
Builder builder = RoleMembership.Builder.create( srmh.rmi);
builder.setRoleSortingCode(StringUtils.leftPad(Integer.toString(group), 3, '0') + "/" + srmh.rmi.getRoleSortingCode() );
newRoleMembers.add(builder.build());
}
return newRoleMembers;
}
protected class SortableRoleMembershipHolder implements Comparable<SortableRoleMembershipHolder> {
public String chart;
public String org;
public RoleMembership rmi;
public SortableRoleMembershipHolder( RoleMembership rmi ) {
chart = rmi.getQualifier().get(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE);
org = rmi.getQualifier().get(KfsKimAttributes.ORGANIZATION_CODE);
Builder builder = RoleMembership.Builder.create(rmi);
builder.setRoleSortingCode(chart+"-"+org);
this.rmi = builder.build();
}
@Override
public int compareTo(SortableRoleMembershipHolder o) {
if ( chart == null || org == null || o.chart == null || o.org == null || (o.chart == chart && o.org == org) ) {
return 0;
}
if ( organizationService.isParentOrganization(o.chart, o.org, chart, org) ) {
return 1;
} else {
return -1;
}
}
}
}