com.sfs.whichdoctor.dao.RelationshipDAOImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.sfs.whichdoctor.dao.RelationshipDAOImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2009 David Harrison.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl-3.0.html
 *
 * Contributors:
 *     David Harrison - initial API and implementation
 ******************************************************************************/
package com.sfs.whichdoctor.dao;

import com.sfs.beans.BuilderBean;
import com.sfs.beans.ObjectTypeBean;
import com.sfs.dao.SFSDaoException;
import com.sfs.whichdoctor.beans.IsbEntityBean;
import com.sfs.whichdoctor.beans.IsbTransactionBean;
import com.sfs.whichdoctor.beans.PersonBean;
import com.sfs.whichdoctor.beans.RelationshipBean;
import com.sfs.whichdoctor.beans.RotationBean;
import com.sfs.whichdoctor.beans.SearchBean;
import com.sfs.whichdoctor.beans.SearchResultsBean;
import com.sfs.whichdoctor.beans.SupervisorBean;
import com.sfs.whichdoctor.formatter.OutputFormatter;
import com.sfs.whichdoctor.search.SearchDAO;
import com.sfs.whichdoctor.search.WhichDoctorSearchDaoException;

import java.sql.ResultSet;
import java.sql.Timestamp;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;

/**
 * The Class RelationshipDAOImpl.
 *
 * @author David Harrison
 */
public class RelationshipDAOImpl extends WhichDoctorBaseDAOImpl implements RelationshipDAO {

    /** The Constant DEFAULT_LIMIT. */
    private static final int DEFAULT_LIMIT = 2;

    /** The Constant LARGE_LONG. */
    private static final long LARGE_LONG = 10000000000L;

    /** The simple date format object. */
    private final SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");

    /** The data logger. */
    private static Logger dataLogger = Logger.getLogger(RelationshipDAOImpl.class);

    /** The previous rotation limit. */
    private int previousRotationLimit = DEFAULT_LIMIT;

    /** The person dao. */
    @Resource
    private PersonDAO personDAO;

    /** The isb transaction dao. */
    @Resource
    private IsbTransactionDAO isbTransactionDAO;

    /** The isb entity dao. */
    @Resource
    private IsbEntityDAO isbEntityDAO;

    /** The search dao. */
    @Resource
    private SearchDAO searchDAO;

    /**
     * Spring injector to override the default number of months should be
     * referred back to in order to find previous rotations supervisors. - The
     * default is 2 months
     *
     * @param previousRotationLimitVal the previous rotation limit
     */
    public final void setPreviousRotationLimit(final int previousRotationLimitVal) {
        this.previousRotationLimit = previousRotationLimitVal;
    }

    /**
     * Used to get a Collection of RelationshipBeans for a specified GUID.
     *
     * @param guid the guid
     * @param loadDetails the load details
     * @return the collection of relationship beans
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    @SuppressWarnings("unchecked")
    public final Collection<RelationshipBean> load(final int guid, final BuilderBean loadDetails)
            throws WhichDoctorDaoException {

        dataLogger.info("Relationships for GUID: " + guid + " requested");

        final String loadRelationships = this.getSQL().getValue("relationship/load")
                + " AND relationships.GUID = ?";

        Collection<RelationshipBean> relationships = new ArrayList<RelationshipBean>();

        try {
            relationships = this.getJdbcTemplateReader().query(loadRelationships, new Object[] { true, guid },
                    new RowMapper() {
                        public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                            return loadRelationship(rs, loadDetails);
                        }
                    });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }
        return relationships;
    }

    /**
     * Used to get a Collection of RelationshipBeans for a specified objecttype.
     *
     * @param objectType the ObjectTypeBean to load
     * @param loadDetails the load details
     * @return the collection< relationship bean>
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    @SuppressWarnings("unchecked")
    public final Collection<RelationshipBean> load(final ObjectTypeBean objectType, final BuilderBean loadDetails)
            throws WhichDoctorDaoException {

        dataLogger.info("Relationships for objectType: " + objectType.getObjectTypeId());

        final String loadRelationships = this.getSQL().getValue("relationship/load")
                + " AND relationships.RelationshipTypeId = ?";

        Collection<RelationshipBean> relationships = new ArrayList<RelationshipBean>();

        try {
            relationships = this.getJdbcTemplateReader().query(loadRelationships,
                    new Object[] { true, objectType.getObjectTypeId() }, new RowMapper() {
                        public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                            return loadRelationship(rs, loadDetails);
                        }
                    });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }
        return relationships;
    }

    /**
     * Returns a map of relationship names ordered by division -> type -> hierarchy.
     *
     * @param relationshipClass - the relationship class to search on
     * @param maximumHierarchy - the largest level of hierarchy to return
     * @return - A map of Division strings with a corresponding Map of supervisor
     *           types and related hierarchies.
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final TreeMap<String, TreeMap<String, Integer>> loadRelationships(final String relationshipClass,
            final int maximumHierarchy) throws WhichDoctorDaoException {

        dataLogger.info("Loading map of '" + relationshipClass + "' relationships types");

        TreeMap<String, TreeMap<String, Integer>> supervisorMap = new TreeMap<String, TreeMap<String, Integer>>();

        int max = maximumHierarchy + 1;

        Collection<RelationshipBean> relationships = loadSupervisors(relationshipClass);

        for (RelationshipBean rel : relationships) {
            if (rel.getHierarchy() > 0 && rel.getHierarchy() < max) {

                TreeMap<String, Integer> map = new TreeMap<String, Integer>();
                if (supervisorMap.containsKey(rel.getDivision())) {
                    map = supervisorMap.get(rel.getDivision());
                }
                map.put(rel.getRelationshipType(), rel.getHierarchy());

                supervisorMap.put(rel.getDivision(), map);
            }
        }
        return supervisorMap;
    }

    /**
     * Used to get a Collection of RelationshipBeans that are dependent on the
     * supplied GUID.
     *
     * @param guid the guid
     * @param loadDetails the load details
     * @return the collection< relationship bean>
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    @SuppressWarnings("unchecked")
    public final Collection<RelationshipBean> findDependencies(final int guid, final BuilderBean loadDetails)
            throws WhichDoctorDaoException {

        dataLogger.info("Dependent relationships for GUID: " + guid + " requested");

        final String loadDependent = this.getSQL().getValue("relationship/findDependent");

        Collection<RelationshipBean> relationships = new ArrayList<RelationshipBean>();

        try {
            relationships = this.getJdbcTemplateReader().query(loadDependent, new Object[] { guid, true },
                    new RowMapper() {
                        public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                            return loadRelationship(rs, loadDetails);
                        }
                    });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }
        return relationships;
    }

    /**
     * Rebuilds the relationships for all active people in the database.
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final void rebuildRelationships() throws WhichDoctorDaoException {

        dataLogger.error("Rebuilding all relationships...");

        /* Perform a people search to get a list of active people */
        SearchBean search = this.searchDAO.initiate("person", null);
        search.setLimit(0);

        final StringBuffer outcome = new StringBuffer();

        try {
            SearchResultsBean results = this.searchDAO.search(search);

            if (results != null) {
                Collection<Object> searchResults = results.getSearchResults();
                for (Object personObj : searchResults) {
                    try {
                        PersonBean person = (PersonBean) personObj;
                        final String result = rebuildRelationships(person.getGUID(), "Person");
                        if (StringUtils.isNotBlank(result)) {
                            outcome.append("\n" + result);
                        }
                    } catch (Exception e) {
                        dataLogger.error("Error rebuilding relationships " + "for person: " + e.getMessage());
                    }
                }
            }
        } catch (WhichDoctorSearchDaoException e) {
            dataLogger.error("Error rebuilding relationships: " + e.getMessage());
            throw new WhichDoctorDaoException("Error rebuilding relationships: " + e.getMessage());
        }

        dataLogger.error("Finished rebuilding all relationships");
        dataLogger.error("Summary of activity: ");
        dataLogger.error(outcome.toString());
    }

    /**
     * Rebuild the relationships for people who may have had their relationships
     * modified during the defined period of time. This function assumes that
     * relationships will be rebuilt when entries are created, modified or
     * deleted. This really just checks rotations that have started or finished
     * within the defined period of time.
     *
     * @param startDate the start date
     * @param endDate the end date
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final void rebuildRelationships(final Date startDate, final Date endDate)
            throws WhichDoctorDaoException {
        if (startDate == null) {
            throw new WhichDoctorDaoException("The start date cannot be null");
        }
        if (endDate == null) {
            throw new WhichDoctorDaoException("The end date cannot be null");
        }

        // A map to store the potentially modified people GUIDs
        HashMap<Integer, Integer> peopleGUIDs = new HashMap<Integer, Integer>();

        // Perform a search for rotations that started between the period of
        // time defined by the startDate and endDate parameters.
        try {
            SearchBean search = this.searchDAO.initiate("rotation", null);
            search.setLimit(0);

            BuilderBean load = new BuilderBean();
            load.setParameter("SUPERVISORS", true);

            RotationBean criteria = (RotationBean) search.getSearchCriteria();
            RotationBean constraints = (RotationBean) search.getSearchConstraints();

            criteria.setStartDate(startDate);
            constraints.setStartDate(endDate);

            search.setSearchCriteria(criteria);
            search.setSearchConstraints(constraints);

            SearchResultsBean results = this.searchDAO.search(search, load);

            if (results != null) {
                Collection<Object> rotations = results.getSearchResults();
                for (Object objRotation : rotations) {
                    RotationBean rotation = (RotationBean) objRotation;
                    if (rotation.getPersonId() > 0) {
                        peopleGUIDs.put(rotation.getPersonId(), 0);
                    }
                    if (rotation.getSupervisors() != null) {
                        for (SupervisorBean sup : rotation.getSupervisors()) {
                            if (sup.getPersonGUID() > 0) {
                                peopleGUIDs.put(sup.getPersonGUID(), 0);
                            }
                        }
                    }
                }
            }
        } catch (WhichDoctorSearchDaoException wse) {
            dataLogger.error("Error performing search for recently started " + "rotations: " + wse.getMessage());
        }

        // Perform a search for rotations that ended between the period of time
        // defined by the startDate and endDate parameters.
        try {
            SearchBean search = this.searchDAO.initiate("rotation", null);
            search.setLimit(0);

            BuilderBean load = new BuilderBean();
            load.setParameter("SUPERVISORS", true);

            RotationBean criteria = (RotationBean) search.getSearchCriteria();
            RotationBean constraints = (RotationBean) search.getSearchConstraints();

            criteria.setEndDate(startDate);
            constraints.setEndDate(endDate);

            search.setSearchCriteria(criteria);
            search.setSearchConstraints(constraints);

            SearchResultsBean results = this.searchDAO.search(search, load);

            if (results != null) {
                Collection<Object> rotations = results.getSearchResults();
                for (Object objRotation : rotations) {
                    RotationBean rotation = (RotationBean) objRotation;
                    if (rotation.getPersonId() > 0) {
                        peopleGUIDs.put(rotation.getPersonId(), 0);
                    }
                    if (rotation.getSupervisors() != null) {
                        for (SupervisorBean sup : rotation.getSupervisors()) {
                            if (sup.getPersonGUID() > 0) {
                                peopleGUIDs.put(sup.getPersonGUID(), 0);
                            }
                        }
                    }
                }
            }
        } catch (WhichDoctorSearchDaoException wse) {
            dataLogger.error("Error performing search for recently started " + "rotations: " + wse.getMessage());
        }

        // With the searches complete iterate through the map and rebuild each
        // of the relationships.
        for (Integer personGUID : peopleGUIDs.keySet()) {
            try {
                dataLogger.info("Rebuilding relationship for person GUID: " + personGUID);
                this.rebuildRelationships(personGUID, "Person");
            } catch (WhichDoctorDaoException wde) {
                dataLogger.error(
                        "Error updating the relationship for person GUID " + personGUID + ": " + wde.getMessage());
            }
        }
    }

    /**
     * Rebuilds the relationship index for the specified GUID.
     *
     * @param guid the guid
     * @param type the type
     * @return the string
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final String rebuildRelationships(final int guid, final String type) throws WhichDoctorDaoException {

        String result = "";

        // Load the existing relationships for this object
        Collection<RelationshipBean> existingRelationships = load(guid, new BuilderBean());
        Collection<RelationshipBean> updatedRelationships = new ArrayList<RelationshipBean>();

        // Begin the ISB transaction */
        IsbTransactionBean isbTransaction = this.isbTransactionDAO.begin(guid);

        if (StringUtils.equalsIgnoreCase(type, "Person")) {
            /* Load the person specified by the GUID */

            BuilderBean loadDetails = new BuilderBean();
            loadDetails.setParameter("MEMBERSHIP", true);
            loadDetails.setParameter("TRAINING_ROTATIONS", true);

            Collection<RelationshipBean> changedRelationships = new ArrayList<RelationshipBean>();

            Collection<RotationBean> priorRotations = new ArrayList<RotationBean>();

            PersonBean person = null;
            try {
                person = this.personDAO.loadGUID(guid, loadDetails);
            } catch (Exception e) {
                dataLogger.error("Error loading person " + guid + ": " + e.getMessage());
            }
            if (person == null) {
                person = new PersonBean();
            }

            Collection<RotationBean> rotations = person.getRotations();
            if (rotations == null) {
                rotations = new ArrayList<RotationBean>();
            }
            Date currentDate = Calendar.getInstance().getTime();

            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MONTH, 0 - this.previousRotationLimit);
            final Date limitMonthsAgo = calendar.getTime();

            for (RotationBean rotation : rotations) {
                try {
                    /* Is this rotation currently taking place? */
                    if (rotation.getStartDate().compareTo(currentDate) <= 0
                            && rotation.getEndDate().compareTo(currentDate) >= 0) {
                        dataLogger.debug("The rotation is taking place now");
                        dataLogger.debug("Rotation supervisor count: " + rotation.getSupervisors().size());

                        for (SupervisorBean supervisor : rotation.getSupervisors()) {
                            RelationshipBean relationship = build(guid, supervisor.getPerson(),
                                    supervisor.getRelationshipClass(), supervisor.getRelationshipType(),
                                    rotation.getEndDate(), rotation.getGUID());
                            changedRelationships.add(relationship);
                        }
                    } else {
                        // If not currently taking place did it end in the
                        // time limit. If so add to prior rotations
                        if (rotation.getEndDate().compareTo(limitMonthsAgo) > 0) {
                            priorRotations.add(rotation);
                        }
                    }
                } catch (Exception e) {
                    dataLogger.error(
                            "Error processing rotation for person (" + person.getGUID() + "): " + e.getMessage());
                }
            }

            // A trainee requires that all supervisor roles are satisfied
            Collection<RelationshipBean> supervisors = new ArrayList<RelationshipBean>();
            Collection<RelationshipBean> otherRelationships = new ArrayList<RelationshipBean>();

            // Divide the relationships between supervisors and others
            for (RelationshipBean relationship : changedRelationships) {
                dataLogger.debug("Relationship class: " + relationship.getRelationshipClass());

                if (StringUtils.endsWith(relationship.getRelationshipClass(), "Supervisor")
                        && StringUtils.isNotBlank(relationship.getISBMapping())) {
                    // The relationship is a supervisor and has an ISB mapping
                    supervisors.add(relationship);
                } else {
                    dataLogger.debug("'Other' relationship processed");
                    otherRelationships.add(relationship);
                }
            }

            // Construct the full list of required supervisors
            Map<Integer, RelationshipBean> fullSupervisorMap = this.buildFullSupervisorMap(supervisors);

            if (fullSupervisorMap.size() == 0) {
                // No supervisors exist - try loading some from the past
                fullSupervisorMap = loadPriorSupervisorMap(priorRotations);
            }

            // Add the relationships from the full supervisors map
            for (Integer hierarchy : fullSupervisorMap.keySet()) {
                RelationshipBean relationship = fullSupervisorMap.get(hierarchy);
                updatedRelationships.add(relationship);
            }
            // Add the relationships from the other relationships collection
            for (RelationshipBean relationship : otherRelationships) {
                updatedRelationships.add(relationship);
            }

            result = update(existingRelationships, updatedRelationships);
        }

        /* Commit the ISB transaction */
        this.isbTransactionDAO.commit(isbTransaction);

        return result;
    }

    /**
     * Generates a map of supervisors for a rotation based on their hierarchy.
     * This function ensures all required supervisor positions are filled.
     *
     * @param rotation the rotation
     *
     * @return the supervisor map
     */
    public final TreeMap<Integer, RelationshipBean> getSupervisorMap(final RotationBean rotation) {

        Collection<RelationshipBean> supervisors = new ArrayList<RelationshipBean>();

        if (rotation != null && rotation.getSupervisors() != null) {
            for (SupervisorBean supervisor : rotation.getSupervisors()) {
                RelationshipBean relationship = build(rotation.getPersonId(), supervisor.getPerson(),
                        supervisor.getRelationshipClass(), supervisor.getRelationshipType(), rotation.getEndDate(),
                        rotation.getGUID());

                supervisors.add(relationship);
            }
        }

        return buildFullSupervisorMap(supervisors);
    }

    /**
     * Given list of prior rotations GUID and build a supervisor map.
     *
     * @param priorRotations the prior rotations
     *
     * @return the tree map< integer, relationship bean>
     */
    private TreeMap<Integer, RelationshipBean> loadPriorSupervisorMap(
            final Collection<RotationBean> priorRotations) {
        TreeMap<Integer, RelationshipBean> supervisorMap = new TreeMap<Integer, RelationshipBean>();

        TreeMap<Long, RotationBean> orderedRotations = new TreeMap<Long, RotationBean>();

        for (RotationBean rotation : priorRotations) {
            long key = LARGE_LONG - rotation.getEndDate().getTime();
            orderedRotations.put(key, rotation);
        }

        for (Long key : orderedRotations.keySet()) {
            RotationBean rotation = orderedRotations.get(key);
            dataLogger.info("End date: " + rotation.getEndDate());
            if (supervisorMap.size() == 0) {
                supervisorMap = getSupervisorMap(rotation);
            }
        }
        return supervisorMap;
    }

    /**
     * Generates a map of supervisors based on their hierarchy. This function
     * ensures all required supervisor positions are filled.
     *
     * @param supervisors as a collection of RelationshipBeans
     *
     * @return the tree map< integer, relationship bean>
     */
    private TreeMap<Integer, RelationshipBean> buildFullSupervisorMap(
            final Collection<RelationshipBean> supervisors) {

        TreeMap<Integer, RelationshipBean> fullSupervisorMap = new TreeMap<Integer, RelationshipBean>();

        TreeMap<Integer, RelationshipBean> btSupervisorMap = new TreeMap<Integer, RelationshipBean>();

        if (supervisors != null) {
            String relationshipClass = "";
            for (RelationshipBean relationship : supervisors) {
                if (StringUtils.startsWithIgnoreCase(relationship.getRelationshipClass(), "Basic")) {
                    // Only one relationship per basic training hierarchy-level is allowed
                    if (dataLogger.isDebugEnabled()) {
                        dataLogger.debug("Relationship: " + relationship.getRelationshipType());
                        dataLogger.debug("Hierarchy: " + relationship.getHierarchy());
                    }
                    relationshipClass = relationship.getRelationshipClass();
                    btSupervisorMap.put(relationship.getHierarchy(), relationship);
                }
            }

            // If a basic training supervisor exists build a full map
            if (btSupervisorMap.size() > 0) {
                dataLogger.debug("At least one basic training supervisor exists");
                dataLogger.debug("Relationship class: " + relationshipClass);
                fullSupervisorMap = buildInheritedMap(relationshipClass, btSupervisorMap);
            }

            for (RelationshipBean relationship : supervisors) {
                if (!StringUtils.startsWithIgnoreCase(relationship.getRelationshipClass(), "Basic")) {
                    String heirarchy = relationship.getHierarchy() + "00" + relationship.getReferenceGUID();
                    int index = Integer.parseInt(heirarchy);
                    fullSupervisorMap.put(index, relationship);
                }
            }
        }

        dataLogger.debug("Supervisor map size: " + fullSupervisorMap.size());

        return fullSupervisorMap;
    }

    /**
     * Builds the inherited map.
     *
     * @param relationshipClass the relationship class
     * @param relationshipMap the relationship map
     * @return the tree map
     */
    private TreeMap<Integer, RelationshipBean> buildInheritedMap(final String relationshipClass,
            final TreeMap<Integer, RelationshipBean> relationshipMap) {

        TreeMap<Integer, RelationshipBean> fullSupervisorMap = new TreeMap<Integer, RelationshipBean>();

        // Load the supervisor type map
        TreeMap<Integer, String> types = null;
        try {
            types = loadSupervisorRelationships(relationshipClass);
        } catch (WhichDoctorDaoException wde) {
            dataLogger.error("Error loading the supervisor object types: " + wde.getMessage());
        }

        int maxHierarchy = 0;
        if (types != null) {
            dataLogger.debug("Supervisor relationships: " + types.size());
            for (Integer hierarchy : types.keySet()) {
                // Set the maxiumum hierarchy value by getting the last key
                if (hierarchy > maxHierarchy) {
                    maxHierarchy = hierarchy;
                }
            }
        }
        dataLogger.debug("Maximum hierarchy: " + maxHierarchy);

        for (Integer hierarchy : types.keySet()) {
            final String typeName = types.get(hierarchy);
            if (dataLogger.isDebugEnabled()) {
                dataLogger.debug("Hierarchy key is: " + hierarchy);
                dataLogger.debug("Type is: " + typeName);
            }
            if (!relationshipMap.containsKey(hierarchy)) {
                // If the relationship is missing we need to find one
                RelationshipBean newSup = null;

                for (int i = 1; i < maxHierarchy + 1; i++) {
                    dataLogger.debug("Looking up key: " + i);
                    if (newSup == null && relationshipMap.containsKey(i)) {
                        RelationshipBean exSup = relationshipMap.get(i);
                        newSup = exSup.clone();
                        newSup.setRelationshipType(typeName);
                    }
                }
                if (newSup != null) {
                    if (dataLogger.isDebugEnabled()) {
                        dataLogger.debug("Adding a new supervisor");
                        dataLogger.debug("GUID: " + newSup.getGUID());
                        dataLogger.debug("Identifier: " + newSup.getIdentifier());
                        dataLogger.debug("Relationship Class: " + newSup.getRelationshipClass());
                        dataLogger.debug("Relationship Type: " + newSup.getRelationshipType());
                    }
                    fullSupervisorMap.put(hierarchy, newSup);
                }
            } else {
                RelationshipBean extSup = relationshipMap.get(hierarchy);
                if (dataLogger.isDebugEnabled()) {
                    dataLogger.debug("Adding an existing supervisor");
                    dataLogger.debug("GUID: " + extSup.getGUID());
                    dataLogger.debug("Identifier: " + extSup.getIdentifier());
                    dataLogger.debug("Relationship Class: " + extSup.getRelationshipClass());
                    dataLogger.debug("Relationship Type: " + extSup.getRelationshipType());
                }
                fullSupervisorMap.put(hierarchy, extSup);
            }
        }
        return fullSupervisorMap;
    }

    /**
     * Iterate through the existing relationships and see what is different to
     * the updated ones.
     *
     * @param existingRelationships the existing relationships
     * @param updatedRelationships the updated relationships
     * @return the string
     */
    private String update(final Collection<RelationshipBean> existingRelationships,
            final Collection<RelationshipBean> updatedRelationships) {

        HashMap<String, RelationshipBean> existingMap = new HashMap<String, RelationshipBean>();
        HashMap<String, RelationshipBean> updatedMap = new HashMap<String, RelationshipBean>();

        for (RelationshipBean relationship : existingRelationships) {
            final String key = buildKey(relationship);
            dataLogger.debug("Adding existing relationship: " + key);
            existingMap.put(key, relationship);
        }
        for (RelationshipBean relationship : updatedRelationships) {
            final String key = buildKey(relationship);
            dataLogger.debug("Adding updated relationship: " + key);
            updatedMap.put(key, relationship);
        }

        dataLogger.debug("Existing map size: " + existingMap.size());
        dataLogger.debug("Updated map size: " + updatedMap.size());

        final StringBuffer summary = new StringBuffer();

        for (String key : existingMap.keySet()) {
            dataLogger.debug("Existing key: " + key);
            // No need to update relationships that have not changed
            if (!updatedMap.containsKey(key)) {
                // The updated map does not contain this entry
                // Delete this relationship reference
                final RelationshipBean relationship = existingMap.get(key);
                try {
                    delete(relationship);

                    // Load the person
                    final PersonBean person = this.personDAO.loadGUID(relationship.getGUID());

                    final StringBuffer info = new StringBuffer();

                    info.append("Deleted a relationship to ");
                    info.append(person.getPersonIdentifier());
                    info.append(". Person ");
                    info.append(relationship.getIdentifier());
                    info.append(" was their ");
                    info.append(relationship.getRelationshipClass());
                    info.append(" - ");
                    info.append(relationship.getRelationshipType());

                    dataLogger.info(info.toString());
                    summary.append(info.toString() + "\n");

                } catch (WhichDoctorDaoException wde) {
                    dataLogger.error("Error deleting relationship: " + wde.getMessage());
                }
            }
        }
        for (String key : updatedMap.keySet()) {
            dataLogger.debug("Updated key: " + key);
            // No need to update relationships that have not changed
            if (!existingMap.containsKey(key)) {
                // The relationship does not exist - create the relationship
                RelationshipBean relationship = updatedMap.get(key);
                try {
                    create(relationship);

                    // Load the person
                    final PersonBean person = this.personDAO.loadGUID(relationship.getGUID());

                    final StringBuffer info = new StringBuffer();

                    info.append("Created a relationship to ");
                    info.append(person.getPersonIdentifier());
                    info.append(". Person ");
                    info.append(relationship.getIdentifier());
                    info.append(" is their ");
                    info.append(relationship.getRelationshipClass());
                    info.append(" - ");
                    info.append(relationship.getRelationshipType());

                    dataLogger.info(info.toString());
                    summary.append(info.toString() + "\n");

                } catch (WhichDoctorDaoException wde) {
                    dataLogger.error("Error creating relationship: " + wde.getMessage());
                }
            }
        }
        return summary.toString();
    }

    /**
     * Builds the RelationshipBean.
     *
     * @param guid the guid
     * @param person the person
     * @param className the class name
     * @param type the type
     * @param rotationEndDate the rotation end date
     *
     * @return the relationship bean
     */
    private RelationshipBean build(final int guid, final PersonBean person, final String className,
            final String type, final Date rotationEndDate, final int rotationGUID) {

        RelationshipBean relationship = new RelationshipBean();

        relationship.setGUID(guid);
        relationship.setReferenceGUID(person.getGUID());
        relationship.setRelationshipClass(className);
        relationship.setRelationshipType(type);
        relationship.setIdentifier(String.valueOf(person.getPersonIdentifier()));
        relationship.setName(OutputFormatter.toFormattedName(person));
        relationship.setPerson(person);
        relationship.setEndDate(rotationEndDate);
        relationship.setRotationGUID(rotationGUID);

        // Load the objecttype for this relationship
        try {
            ObjectTypeBean objecttype = this.getObjectTypeDAO().load("Relationship Type", type, className);
            if (objecttype != null) {
                relationship.setISBMapping(objecttype.getLdapMapping(1));
                relationship.setHierarchy((int) objecttype.getValue());
            }
        } catch (SFSDaoException sde) {
            dataLogger.info("Error loading objecttype for relationship: " + sde.getMessage());
        }

        return relationship;
    }

    /**
     * Builds the key.
     *
     * @param relationship the relationship
     *
     * @return the string
     */
    private String buildKey(final RelationshipBean relationship) {

        String endDate = "";

        if (relationship.getEndDate() != null) {
            endDate = df.format(relationship.getEndDate());
        }

        String key = relationship.getGUID() + "_" + relationship.getReferenceGUID() + "_"
                + relationship.getRelationshipClass() + "_" + relationship.getRelationshipType() + "_" + endDate;

        return key;
    }

    /**
     * Creates the.
     *
     * @param relationship the relationship
     *
     * @return true, if successful
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private boolean create(final RelationshipBean relationship) throws WhichDoctorDaoException {
        return update(relationship, true);
    }

    /**
     * Delete.
     *
     * @param relationship the relationship
     *
     * @return true, if successful
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private boolean delete(final RelationshipBean relationship) throws WhichDoctorDaoException {
        return update(relationship, false);
    }

    /**
     * Update.
     *
     * @param relationship the relationship
     * @param status the status
     *
     * @return true, if successful
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private boolean update(final RelationshipBean relationship, final boolean status)
            throws WhichDoctorDaoException {

        boolean success = false;

        // Get the relationship Type
        int relationshipTypeId = 0;
        try {
            ObjectTypeBean object = this.getObjectTypeDAO().load("Relationship Type",
                    relationship.getRelationshipType(), relationship.getRelationshipClass());
            relationshipTypeId = object.getObjectTypeId();
        } catch (Exception e) {
            throw new WhichDoctorDaoException("Error loading relationship type: " + e.getMessage());
        }

        Timestamp sqlTimeStamp = new Timestamp(Calendar.getInstance().getTimeInMillis());

        final int updateCount = this.getJdbcTemplateWriter().update(this.getSQL().getValue("relationship/update"),
                new Object[] { relationship.getGUID(), relationship.getReferenceGUID(), relationshipTypeId,
                        relationship.getIdentifier(), relationship.getName(), relationship.getEndDate(),
                        relationship.getRotationGUID(), sqlTimeStamp, status, relationship.getIdentifier(),
                        relationship.getName(), relationship.getEndDate(), relationship.getRotationGUID(),
                        sqlTimeStamp, status });

        if (updateCount > 0) {

            success = true;

            // With the relationships updated check that
            // all the related entities are on the ISB
            try {
                // See if this the GUID of this relationship is an ISB entity
                // Load the current list of relationships this GUID has
                Collection<RelationshipBean> relationships = load(relationship.getGUID(), new BuilderBean());

                /* Load the references to this relationship's GUID on the ISB */
                dataLogger.debug("Loading the relationships for GUID: " + relationship.getGUID());

                Collection<IsbEntityBean> entities = new ArrayList<IsbEntityBean>();

                SearchBean search = searchDAO.initiate("isbentity", null);
                IsbEntityBean searchCriteria = (IsbEntityBean) search.getSearchCriteria();
                searchCriteria.setGUID(relationship.getGUID());
                search.setSearchCriteria(searchCriteria);
                search.setLimit(0);

                SearchResultsBean results = searchDAO.search(search);
                if (results != null && results.getSearchResults() != null) {
                    for (Object result : results.getSearchResults()) {
                        IsbEntityBean isbEntity = (IsbEntityBean) result;
                        entities.add(isbEntity);
                    }
                }

                // Iterate through the relationships
                // and entities to ensure sibling entities exist
                for (RelationshipBean related : relationships) {
                    // The relationship has an ISB mapping
                    if (StringUtils.isNotBlank(related.getISBMapping())) {
                        for (IsbEntityBean entity : entities) {
                            dataLogger.debug("Creating entity GUID: " + related.getGUID() + " for target: "
                                    + entity.getTarget());
                            try {
                                // Create this related entity
                                // on the ISB (if exists won't be created)
                                IsbEntityBean newEntity = new IsbEntityBean();
                                newEntity.setGUID(related.getReferenceGUID());
                                newEntity.setIdentifier(related.getIdentifier());
                                newEntity.setName(related.getName());
                                newEntity.setObjectType("Person");
                                newEntity.setTarget(entity.getTarget());

                                this.isbEntityDAO.create(newEntity, false);
                            } catch (Exception e) {
                                dataLogger.error("Error creating related ISB " + "entity: " + e.getMessage());
                            }
                        }
                    }
                }
            } catch (Exception e) {
                dataLogger.error("Error checking related entities on the ISB: " + e.getMessage());
            }
        }
        return success;
    }

    /**
     * Load relationship from the supplied dataset.
     *
     * @param rs the rs
     * @param loadDetails the load details
     * @return the relationship bean
     *
     * @throws SQLException the SQL exception
     */
    private RelationshipBean loadRelationship(final ResultSet rs, final BuilderBean loadDetails)
            throws SQLException {

        // Create relationship bean and fill with dataset info.
        RelationshipBean relationship = new RelationshipBean();

        relationship.setGUID(rs.getInt("GUID"));
        relationship.setReferenceGUID(rs.getInt("ReferenceGUID"));
        relationship.setRelationshipClass(rs.getString("RelationshipClass"));
        relationship.setRelationshipType(rs.getString("RelationshipType"));
        relationship.setHierarchy(rs.getInt("Hierarchy"));
        relationship.setISBMapping(rs.getString("ISBMapping"));

        relationship.setIdentifier(rs.getString("Identifier"));
        relationship.setName(rs.getString("Name"));
        relationship.setRotationGUID(rs.getInt("RotationGUID"));
        try {
            relationship.setEndDate(rs.getDate("EndDate"));
        } catch (SQLException e) {
            dataLogger.info("Error parsing EndDate date: " + e.getMessage());
        }
        try {
            relationship.setModifiedDate(rs.getDate("Modified"));
        } catch (SQLException e) {
            dataLogger.info("Error parsing Modified date: " + e.getMessage());
        }

        if (loadDetails.getBoolean("PERSON")) {
            try {
                PersonBean person = this.personDAO.loadGUID(relationship.getGUID());
                relationship.setPerson(person);
            } catch (WhichDoctorDaoException wde) {
                dataLogger.error("Error loading person for relationship: " + wde.getMessage());
            }
        }
        if (loadDetails.getBoolean("RELATED_PERSON")) {
            try {
                PersonBean person = this.personDAO.loadGUID(relationship.getReferenceGUID());
                relationship.setRelatedPerson(person);
            } catch (WhichDoctorDaoException wde) {
                dataLogger.error("Error loading related person for relationship: " + wde.getMessage());
            }
        }

        return relationship;
    }

    /**
     * Load supervisor relationships.
     *
     * @param relationshipClass the relationship class
     * @return the tree map<integer, string>
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private TreeMap<Integer, String> loadSupervisorRelationships(final String relationshipClass)
            throws WhichDoctorDaoException {

        dataLogger.info("Loading map of supervisor relationships types");

        TreeMap<Integer, String> supervisorMap = new TreeMap<Integer, String>();

        Collection<RelationshipBean> relationships = loadSupervisors(relationshipClass);

        for (RelationshipBean relationship : relationships) {
            if (relationship.getHierarchy() > 0 && !supervisorMap.containsKey(relationship.getHierarchy())) {
                supervisorMap.put(relationship.getHierarchy(), relationship.getRelationshipType());
            }
        }
        return supervisorMap;
    }

    /**
     * A private function to load the collection of supervisor
     * RelationshipBeans.
     *
     * @param relationshipClass the relatinship class
     * @return the collection< relationship bean>
     */
    @SuppressWarnings("unchecked")
    private Collection<RelationshipBean> loadSupervisors(final String relationshipClass) {

        final String loadSupervisors = this.getSQL().getValue("relationship/loadSupervisors");

        Collection<RelationshipBean> relationships = new ArrayList<RelationshipBean>();

        try {
            relationships = this.getJdbcTemplateReader().query(loadSupervisors,
                    new Object[] { "Relationship Type", relationshipClass }, new RowMapper() {
                        public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {

                            RelationshipBean rel = new RelationshipBean();

                            rel.setRelationshipClass(rs.getString("RelationshipClass"));
                            rel.setRelationshipType(rs.getString("RelationshipType"));
                            rel.setDivision(rs.getString("Division"));
                            rel.setHierarchy(rs.getInt("Hierarchy"));

                            return rel;
                        }
                    });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }

        return relationships;
    }
}