org.kuali.kra.service.impl.ResearchAreasServiceBaseImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kra.service.impl.ResearchAreasServiceBaseImpl.java

Source

/*
 * Kuali Coeus, a comprehensive research administration system for higher education.
 * 
 * Copyright 2005-2015 Kuali, Inc.
 * 
 * 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.kra.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.kuali.coeus.common.committee.impl.bo.CommitteeBase;
import org.kuali.coeus.common.committee.impl.bo.CommitteeMembershipBase;
import org.kuali.coeus.common.committee.impl.bo.CommitteeMembershipExpertiseBase;
import org.kuali.coeus.common.committee.impl.bo.CommitteeResearchAreaBase;
import org.kuali.coeus.sys.framework.service.KcServiceLocator;
import org.kuali.kra.bo.ResearchAreaBase;
import org.kuali.kra.dao.ResearchAreaReferencesDao;
import org.kuali.kra.protocol.ProtocolBase;
import org.kuali.kra.protocol.protocol.research.ProtocolResearchAreaBase;
import org.kuali.kra.service.ResearchAreaCurrentReferencerHolderBase;
import org.kuali.kra.service.ResearchAreasServiceBase;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.KRADConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class ResearchAreasServiceBaseImpl implements ResearchAreasServiceBase {

    private static final String COLUMN_CODE_1 = "%3A";
    private static final String COLUMN_CODE_2 = "%4A";
    private static final String CREATE_RESEARCH_AREA = "RaCreate";
    private static final String UPDATE_PARENT_RESEARCH_AREA = "RaUpdateParent";
    private static final String UPDATE_RESEARCH_AREA_ACTIVE_FLAG = "RaUpdateActiveIndicator";
    private static final String UPDATE_RESEARCH_AREA_DESCRIPTION = "RaUpdateDescription";
    private static final String RESEARCH_AREA_CODE = "researchAreaCode";
    private static final String COMMITTEE_ID = "committeeId";
    private static final String SEQUENCE_NUMBER = "sequenceNumber";
    private static final String CODE = "Code";
    private static final String TRUE = "true";
    private static final String ACTIVE = "Active";
    private static final String DESCRIPTION = "Description";
    private static final String PARENT_CODE = "ParentCode";
    private static final String PARENT_RESEARCH_AREA_CODE = "parentResearchAreaCode";
    private static final String NEW_PARENT = "NewParent";
    private BusinessObjectService businessObjectService;
    private ResearchAreaReferencesDao researchAreaReferencesDao;

    @Override
    public String getSubResearchAreasForTreeView(String researchAreaCode, boolean activeOnly) {
        String researchAreas = "<h3>";
        for (ResearchAreaBase researchArea : getSubResearchAreas(researchAreaCode, activeOnly)) {
            researchAreas = researchAreas + researchArea.getResearchAreaCode() + KRADConstants.BLANK_SPACE
                    + COLUMN_CODE_1 + KRADConstants.BLANK_SPACE + researchArea.getDescription()
                    + KRADConstants.BLANK_SPACE + COLUMN_CODE_2 + KRADConstants.BLANK_SPACE
                    + researchArea.isActive() + "</h3><h3>";
        }
        researchAreas = researchAreas.substring(0, researchAreas.length() - 4);
        return researchAreas;

    }

    /*
     * call businessobjectservice to get a list of sub research areas of 'researchareacode'
     */
    @SuppressWarnings("unchecked")
    protected List<ResearchAreaBase> getSubResearchAreas(String researchAreaCode, boolean activeOnly) {
        List<ResearchAreaBase> researchAreasList = new ArrayList<ResearchAreaBase>();
        Map<String, Object> fieldValues = new HashMap<String, Object>();
        fieldValues.put(PARENT_RESEARCH_AREA_CODE, researchAreaCode);
        if (activeOnly) {
            fieldValues.put("active", activeOnly);
        }
        researchAreasList.addAll(businessObjectService.findMatchingOrderBy(getResearchAreaBOClassHook(),
                fieldValues, RESEARCH_AREA_CODE, true));
        return researchAreasList;
    }

    @Override
    public boolean isResearchAreaExist(String researchAreaCode, String researchAreas) {
        Map<String, Object> fieldValues = new HashMap<String, Object>();
        fieldValues.put(RESEARCH_AREA_CODE, researchAreaCode);
        boolean isExist = businessObjectService.findByPrimaryKey(getResearchAreaBOClassHook(), fieldValues) != null;
        if (isExist && StringUtils.isNotBlank(researchAreas)) {
            for (String raCode : researchAreas.split(";")) {
                if (raCode.equals(researchAreaCode)) {
                    isExist = false;
                    break;
                } else {
                    if (isExistInDeletedChildren(researchAreaCode, raCode)) {
                        isExist = false;
                        break;
                    }
                }
            }

        }
        return isExist;
    }

    /**
     * @see org.kuali.kra.irb.service.ResearchAreasService#checkResearchAreaAndDescendantsNotReferenced(java.lang.String)
     * TODO: optimize by checking only active descendants? Could use the getSubResearchAreas method above
     */
    public boolean checkResearchAreaAndDescendantsNotReferenced(String researchAreaCode) {
        boolean retValue = true;
        ResearchAreaBase researchArea = getBusinessObjectService()
                .findBySinglePrimaryKey(getResearchAreaBOClassHook(), researchAreaCode);
        if ((researchArea != null)) {
            retValue = checkResearchAreaTree(researchArea);
        } else {
            // TODO throw exception? 
        }
        return retValue;
    }

    // recursive method to check the tree in depth-first fashion
    @SuppressWarnings("unchecked")
    private boolean checkResearchAreaTree(ResearchAreaBase researchArea) {
        boolean retValue = true;
        String researchAreaCode = researchArea.getResearchAreaCode();
        if ((this.getResearchAreaReferencesDao().isResearchAreaReferencedByAnyCommittee(researchAreaCode))
                || (this.getResearchAreaReferencesDao()
                        .isResearchAreaReferencedByAnyCommitteeMember(researchAreaCode))
                || (this.getResearchAreaReferencesDao().isResearchAreaReferencedByAnyProtocol(researchAreaCode))) {
            retValue = false;
        } else {
            // get the children of this research area
            Map<String, String> fieldValues = new HashMap<String, String>();
            fieldValues.put(PARENT_RESEARCH_AREA_CODE, researchAreaCode);
            List<ResearchAreaBase> subResearchAreas = (List<ResearchAreaBase>) getBusinessObjectService()
                    .findMatching(getResearchAreaBOClassHook(), fieldValues);
            for (ResearchAreaBase subResearchArea : subResearchAreas) {
                // recursive call
                if (false == checkResearchAreaTree(subResearchArea)) {
                    // no need to check remaining children
                    retValue = false;
                    break;
                }
            }
        }
        return retValue;
    }

    @Override
    public void deleteResearchAreaAndDescendants(String researchAreaCode) {
        ResearchAreaBase researchArea = getBusinessObjectService()
                .findBySinglePrimaryKey(getResearchAreaBOClassHook(), researchAreaCode);
        if (researchArea != null) {
            getBusinessObjectService().delete(researchArea);
            deleteChildrenResearchAreas(researchAreaCode);
            updateHasChildrenFlag(researchArea.getParentResearchAreaCode());
        }
    }

    /**
     * This method recursively deletes all child research areas.
     * @param parentResearchAreaCode
     */
    @SuppressWarnings("unchecked")
    private void deleteChildrenResearchAreas(String parentResearchAreaCode) {
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(PARENT_RESEARCH_AREA_CODE, parentResearchAreaCode);
        List<ResearchAreaBase> researchAreas = (List<ResearchAreaBase>) businessObjectService
                .findMatching(getResearchAreaBOClassHook(), fieldValues);
        for (ResearchAreaBase researchArea : researchAreas) {
            deleteChildrenResearchAreas(researchArea.getResearchAreaCode());
            businessObjectService.delete(researchArea);
        }
    }

    /**
     * This method determines if the hasChildrenFlag needs to be set or cleared.
     * @param researchAreaCode
     */
    private void updateHasChildrenFlag(String researchAreaCode) {
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(PARENT_RESEARCH_AREA_CODE, researchAreaCode);
        if (businessObjectService.countMatching(getResearchAreaBOClassHook(), fieldValues) > 0) {
            setHasChildrenFlag(researchAreaCode, true);
        } else {
            setHasChildrenFlag(researchAreaCode, false);
        }
    }

    /**
     * This method sets the hasChildrenFlag to a specific value.
     * @param researchAreaCode
     * @param hasChildrenFlag
     */
    private void setHasChildrenFlag(String researchAreaCode, boolean hasChildrenFlag) {
        ResearchAreaBase researchArea = businessObjectService.findBySinglePrimaryKey(getResearchAreaBOClassHook(),
                researchAreaCode);
        if (researchArea.getHasChildrenFlag() != hasChildrenFlag) {
            researchArea.setHasChildrenFlag(hasChildrenFlag);
            businessObjectService.save(researchArea);
        }
    }

    /*
     * This method is a recursive call to check whether the new 'researchAreaCode' matched
     * raCode's Descendants' code
     */
    protected boolean isExistInDeletedChildren(String researchAreaCode, String raCode) {
        boolean isExist = false;
        for (ResearchAreaBase researchArea : getSubResearchAreas(raCode, false)) {
            if (researchAreaCode.equals(researchArea.getResearchAreaCode())) {
                isExist = true;
                break;
            } else {
                isExist = isExistInDeletedChildren(researchAreaCode, researchArea.getResearchAreaCode());
            }
        }
        return isExist;
    }

    /**
     * The format is as follows:
     * <pre>
     * {@code
     * <RaChanges>
     *   <RaChangesElement>
     *       <RaCreate>
     *           <Code></Code>
     *           <ParentCode></ParentCode>
     *           <Description></Description>
     *           <Active></Active>
     *       </RaCreate>
     *       <RaUpdateDescription>
     *           <Code></Code>
     *           <Description></Description>
     *       </RaUpdateDescription>
     *       <RaUpdateActiveIndicator>
     *           <Code></Code>
     *           <Active></Active>
     *       </RaUpdateActiveIndicator>
     *       <RaUpdateParent>
     *           <Code></Code>
     *           <OldParent></OldParent>
     *           <NewParent></NewParent>
     *       </RaUpdateParent>
     *       <RaDelete>
     *           <Code></Code>
     *       </RaDelete>
     *   </RaChangesElement>
     * </RaChanges>
     * }
     * </pre>
     * @throws SAXException 
     * @throws IOException 
     * @throws ParserConfigurationException 
     * 
     * @see org.kuali.kra.irb.service.ResearchAreasService#saveResearchAreas(java.lang.String)
     */
    public void saveResearchAreas(String raChangeXML)
            throws ParserConfigurationException, IOException, SAXException {
        InputSource is = new InputSource();
        is.setCharacterStream(new StringReader(raChangeXML));
        Document raChangesDocument;
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        raChangesDocument = db.parse(is);
        List<Element> raChanges = getRaChanges(raChangesDocument);
        for (Element raChange : raChanges) {
            Map<String, Map<String, String>> details = getRaChangeDetails(raChange);
            if (details.containsKey(CREATE_RESEARCH_AREA)) {
                boolean active = StringUtils.equalsIgnoreCase(details.get(CREATE_RESEARCH_AREA).get(ACTIVE), TRUE)
                        ? true
                        : false;

                /*
                ResearchArea researchArea = new ResearchArea(details.get(CREATE_RESEARCH_AREA).get(CODE), details.get(CREATE_RESEARCH_AREA)
                    .get(PARENT_CODE), details.get(CREATE_RESEARCH_AREA).get(DESCRIPTION), active);
                    */

                ResearchAreaBase researchArea = getNewResearchAreaInstanceHook(
                        details.get(CREATE_RESEARCH_AREA).get(CODE),
                        details.get(CREATE_RESEARCH_AREA).get(PARENT_CODE),
                        details.get(CREATE_RESEARCH_AREA).get(DESCRIPTION), active);

                businessObjectService.save(researchArea);
                setHasChildrenFlag(details.get(CREATE_RESEARCH_AREA).get(PARENT_CODE), true);

            }
            if (details.containsKey(UPDATE_RESEARCH_AREA_DESCRIPTION)) {
                ResearchAreaBase researchArea = businessObjectService.findBySinglePrimaryKey(
                        getResearchAreaBOClassHook(), details.get(UPDATE_RESEARCH_AREA_DESCRIPTION).get(CODE));
                researchArea.setDescription(details.get(UPDATE_RESEARCH_AREA_DESCRIPTION).get(DESCRIPTION));
                businessObjectService.save(researchArea);
            }
            if (details.containsKey(UPDATE_RESEARCH_AREA_ACTIVE_FLAG)) {
                // with kcirb-1424's changes, the 'update' will now always be an activation (deactivations are carried out seperately).
                // boolean active = StringUtils.equalsIgnoreCase(details.get(UPDATE_RESEARCH_AREA_ACTIVE_FLAG).get(ACTIVE), TRUE) ? true : false;
                ResearchAreaBase researchArea = businessObjectService.findBySinglePrimaryKey(
                        getResearchAreaBOClassHook(), details.get(UPDATE_RESEARCH_AREA_ACTIVE_FLAG).get(CODE));
                researchArea.setActive(true);
                businessObjectService.save(researchArea);
                // inactivateChildrenResearchAreas(details.get(UPDATE_RESEARCH_AREA_ACTIVE_FLAG).get(CODE));
            }
            if (details.containsKey(UPDATE_PARENT_RESEARCH_AREA)) {
                ResearchAreaBase researchArea = businessObjectService.findBySinglePrimaryKey(
                        getResearchAreaBOClassHook(), details.get(UPDATE_PARENT_RESEARCH_AREA).get(CODE));
                researchArea.setParentResearchAreaCode(details.get(UPDATE_PARENT_RESEARCH_AREA).get(NEW_PARENT));
                businessObjectService.save(researchArea);
                setHasChildrenFlag(details.get(UPDATE_PARENT_RESEARCH_AREA).get(NEW_PARENT), true);
                updateHasChildrenFlag(details.get(UPDATE_PARENT_RESEARCH_AREA).get("OldParent"));
            }
        }
    }

    List<Element> getRaChanges(Document doc) {
        List<Element> elements = new ArrayList<Element>();
        NodeList nodes = doc.getElementsByTagName("RaChangesElement");
        for (int i = 0; i < nodes.getLength(); i++) {
            if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                elements.add((Element) nodes.item(i));
            }
        }
        return elements;
    }

    /**
     * This method populates the research area change details.
     * The map contains the change type as the key and a map of the detail attributes (see raChangeAttribute()).
     * e.g. [RaUpdateActiveIndicator", ["Code", "01."], ["Active", "false"]]
     * @param raChange
     * @return map with the research area details.
     */
    Map<String, Map<String, String>> getRaChangeDetails(Element raChange) {
        Map<String, Map<String, String>> raChangeDetails = new HashMap<String, Map<String, String>>();
        NodeList raChangeDetail = raChange.getChildNodes();
        for (int i = 0; i < raChangeDetail.getLength(); i++) {
            if ((raChangeDetail.item(i).getNodeType() == Node.ELEMENT_NODE)) {
                raChangeDetails.put(raChangeDetail.item(i).getNodeName(),
                        raChangeAttribute(raChangeDetail.item(i)));
            }
        }
        return raChangeDetails;
    }

    /**
     * This method gets the detail attributes for a specific change.
     * The map contains the attribute name as the key and attribute value as the value.
     * e.g. ["code", "01.123"], ["ParentCode", "01."], ["Description","This is a sample"],["Active", "true"]  
     * @param raChangeDetail
     * @return map of the research area change attribute's names and values;
     */
    private Map<String, String> raChangeAttribute(Node raChangeDetail) {
        Map<String, String> raChangeAttributes = new HashMap<String, String>();
        NodeList attributes = raChangeDetail.getChildNodes();
        for (int i = 0; i < attributes.getLength(); i++) {
            if (attributes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                raChangeAttributes.put(attributes.item(i).getNodeName(),
                        attributes.item(i).getFirstChild().getNodeValue());
            }
        }
        return raChangeAttributes;
    }

    @Override
    public ProtocolBase getCurrentProtocolReferencingResearchArea(String researchAreaCode) {
        ProtocolBase retValue = null;
        // get the collection of all ProtocolResearchAreaBase instances that have the given research area code
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(RESEARCH_AREA_CODE, researchAreaCode);
        @SuppressWarnings("unchecked")
        List<ProtocolResearchAreaBase> pras = (List<ProtocolResearchAreaBase>) this.getBusinessObjectService()
                .findMatching(getProtocolResearchAreaBOClassHook(), fieldValues);
        // loop through the collection checking the parent protocol of each instance for 'currentness'
        for (ProtocolResearchAreaBase pra : pras) {
            // get the parent protocol instance via getter (it is populated because of auto-retrieve in repository)
            ProtocolBase parentProtocol = pra.getProtocol();
            //check if protocol is active
            if ((null != parentProtocol) && parentProtocol.isActive()) {
                retValue = parentProtocol;
                break;
            }
        }
        return retValue;
    }

    // helper method that checks that the given committee instance has the highest sequence number of all other 
    // committee instances in the database with the same committee id.
    // note: See replacement for this method in ResearchAreaReferencesDaoOjb if efficiency becomes a concern
    private boolean isCurrentVersion(CommitteeBase committee) {
        boolean retValue = false;
        // get the list of all CommitteeBase instances that have the same id as the argument instance, 
        // sorted in descending order of their sequence numbers
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(COMMITTEE_ID, committee.getCommitteeId());
        @SuppressWarnings("unchecked")
        List<CommitteeBase> committees = (List<CommitteeBase>) this.getBusinessObjectService()
                .findMatchingOrderBy(getCommitteeBOClassHook(), fieldValues, SEQUENCE_NUMBER, false);
        // check the first element's sequence number with the argument's sequence number
        if ((committees != null) && (!committees.isEmpty())
                && (committees.get(0).getSequenceNumber().equals(committee.getSequenceNumber()))) {
            retValue = true;
        }
        return retValue;
    }

    @Override
    public CommitteeBase getCurrentCommitteeReferencingResearchArea(String researchAreaCode) {
        CommitteeBase retValue = null;
        // get the collection of all CommitteeResearchAreaBase instances that have the given research area code
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(RESEARCH_AREA_CODE, researchAreaCode);
        @SuppressWarnings("unchecked")
        List<CommitteeResearchAreaBase> cras = (List<CommitteeResearchAreaBase>) this.getBusinessObjectService()
                .findMatching(getCommitteeResearchAreaBOClassHook(), fieldValues);
        // loop through the collection checking the parent committee of each instance for currentness
        for (CommitteeResearchAreaBase cra : cras) {
            // get the parent committee using the FK (auto-retrieve is false in the repository)
            CommitteeBase parentCommittee = this.getBusinessObjectService()
                    .findBySinglePrimaryKey(getCommitteeBOClassHook(), cra.getCommitteeIdFk());
            // check if the committee is the current version
            if ((null != parentCommittee) && this.isCurrentVersion(parentCommittee)) {
                retValue = parentCommittee;
                break;
            }
        }
        return retValue;
    }

    @Override
    public CommitteeMembershipBase getCurrentCommitteeMembershipReferencingResearchArea(String researchAreaCode) {
        CommitteeMembershipBase retValue = null;
        // get the collection of all CommitteeMembershipExpertise instances that have the given research area code
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(RESEARCH_AREA_CODE, researchAreaCode);
        @SuppressWarnings("unchecked")
        List<CommitteeMembershipExpertiseBase> cmes = (List<CommitteeMembershipExpertiseBase>) this
                .getBusinessObjectService().findMatching(getCommitteeMembershipExpertiseClassHook(), fieldValues);
        // loop through the collection checking the parent committee of each instance for currentness
        for (CommitteeMembershipExpertiseBase cme : cmes) {
            // first get the parent committee membership using the FK
            CommitteeMembershipBase parentCommitteeMembership = this.getBusinessObjectService()
                    .findBySinglePrimaryKey(getCommitteeMembershipBOClassHook(), cme.getCommitteeMembershipIdFk());
            // check if the parent committee membership's term is still open
            if (null != parentCommitteeMembership && (!parentCommitteeMembership.hasTermEnded())) {
                // then get the parent committee using the FK
                CommitteeBase parentCommittee = this.getBusinessObjectService().findBySinglePrimaryKey(
                        getCommitteeBOClassHook(), parentCommitteeMembership.getCommitteeIdFk());
                // check if the committee is the current version
                if ((null != parentCommittee) && this.isCurrentVersion(parentCommittee)) {
                    retValue = parentCommitteeMembership;
                    break;
                }
            }
        }
        return retValue;
    }

    protected abstract Class<? extends CommitteeMembershipBase> getCommitteeMembershipBOClassHook();

    protected abstract Class<? extends CommitteeMembershipExpertiseBase> getCommitteeMembershipExpertiseClassHook();

    /**
     * This method recursively inactivates all child research areas.
     * @param parentResearchAreaCode
     */
    @SuppressWarnings("unchecked")
    private void inactivateChildrenResearchAreas(String parentResearchAreaCode) {
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(PARENT_RESEARCH_AREA_CODE, parentResearchAreaCode);
        List<ResearchAreaBase> researchAreas = (List<ResearchAreaBase>) businessObjectService
                .findMatching(getResearchAreaBOClassHook(), fieldValues);
        for (ResearchAreaBase researchArea : researchAreas) {
            inactivateChildrenResearchAreas(researchArea.getResearchAreaCode());
            researchArea.setActive(false);
            businessObjectService.save(researchArea);
        }
    }

    @Override
    public void deactivateResearchAreaAndDescendants(String researchAreaCode) throws Exception {
        ResearchAreaBase researchArea = businessObjectService.findBySinglePrimaryKey(getResearchAreaBOClassHook(),
                researchAreaCode);
        if (researchArea != null) {
            researchArea.setActive(false);
            businessObjectService.save(researchArea);
            inactivateChildrenResearchAreas(researchAreaCode);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public ResearchAreaCurrentReferencerHolderBase getAnyCurrentReferencerForResearchAreaOrDescendant(
            String researchAreaCode) {
        ResearchAreaCurrentReferencerHolderBase retValue = ResearchAreaCurrentReferencerHolderBase.NO_REFERENCER;
        ProtocolBase referencingProtocol = this.getCurrentProtocolReferencingResearchArea(researchAreaCode);
        if (null != referencingProtocol) {
            retValue = new ResearchAreaCurrentReferencerHolderBase(researchAreaCode, referencingProtocol, null,
                    null);
        } else {
            CommitteeBase referencingCommittee = this.getCurrentCommitteeReferencingResearchArea(researchAreaCode);
            if (null != referencingCommittee) {
                retValue = new ResearchAreaCurrentReferencerHolderBase(researchAreaCode, null, referencingCommittee,
                        null);
            } else {
                CommitteeMembershipBase referencingCommitteeMembership = this
                        .getCurrentCommitteeMembershipReferencingResearchArea(researchAreaCode);
                if (null != referencingCommitteeMembership) {
                    CommitteeBase parentCommittee = this.getBusinessObjectService().findBySinglePrimaryKey(
                            getCommitteeBOClassHook(), referencingCommitteeMembership.getCommitteeIdFk());
                    retValue = new ResearchAreaCurrentReferencerHolderBase(researchAreaCode, null, parentCommittee,
                            referencingCommitteeMembership);
                } else {
                    // if we came here, then this RA is not referenced, so check its active descendants recursively
                    Map<String, Object> fieldValues = new HashMap<String, Object>();
                    fieldValues.put(PARENT_RESEARCH_AREA_CODE, researchAreaCode);
                    fieldValues.put("active", true);
                    List<ResearchAreaBase> subResearchAreas = (List<ResearchAreaBase>) getBusinessObjectService()
                            .findMatching(getResearchAreaBOClassHook(), fieldValues);

                    for (ResearchAreaBase subResearchArea : subResearchAreas) {
                        // recursive call
                        retValue = getAnyCurrentReferencerForResearchAreaOrDescendant(
                                subResearchArea.getResearchAreaCode());
                        if (retValue != ResearchAreaCurrentReferencerHolderBase.NO_REFERENCER) {
                            // no need to check remaining children
                            break;
                        }
                    }
                }
            }
        }
        return retValue;
    }

    public BusinessObjectService getBusinessObjectService() {
        return businessObjectService;
    }

    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    public ResearchAreaReferencesDao getResearchAreaReferencesDao() {
        if (null == this.researchAreaReferencesDao) {
            this.setResearchAreaReferencesDao(KcServiceLocator.getService(ResearchAreaReferencesDao.class));
        }
        return researchAreaReferencesDao;
    }

    public void setResearchAreaReferencesDao(ResearchAreaReferencesDao researchAreaReferencesDao) {
        this.researchAreaReferencesDao = researchAreaReferencesDao;
    }

    protected abstract Class<? extends CommitteeBase> getCommitteeBOClassHook();

    protected abstract Class<? extends CommitteeResearchAreaBase> getCommitteeResearchAreaBOClassHook();

    protected abstract Class<? extends ProtocolResearchAreaBase> getProtocolResearchAreaBOClassHook();

    protected abstract Class<? extends ResearchAreaBase> getResearchAreaBOClassHook();

    protected abstract ResearchAreaBase getNewResearchAreaInstanceHook(String researchAreaCode,
            String parentResearchAreaCode, String description, boolean active);

}