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

Java tutorial

Introduction

Here is the source code for com.sfs.whichdoctor.dao.MembershipDAOImpl.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.FieldMapBean;
import com.sfs.beans.ObjectTypeBean;
import com.sfs.beans.PrivilegesBean;
import com.sfs.beans.UserBean;
import com.sfs.dao.FieldMapDAO;
import com.sfs.dao.PrivilegesDAO;
import com.sfs.dao.SFSDaoException;
import com.sfs.whichdoctor.beans.AccreditationBean;
import com.sfs.whichdoctor.beans.ExamBean;
import com.sfs.whichdoctor.beans.IsbTransactionBean;
import com.sfs.whichdoctor.beans.MembershipBean;
import com.sfs.whichdoctor.beans.PersonBean;
import com.sfs.whichdoctor.beans.PreferencesBean;
import com.sfs.whichdoctor.beans.RotationBean;
import com.sfs.whichdoctor.beans.SearchBean;
import com.sfs.whichdoctor.beans.SearchResultsBean;
import com.sfs.whichdoctor.beans.SpecialtyBean;
import com.sfs.whichdoctor.search.SearchDAO;
import com.sfs.whichdoctor.search.WhichDoctorSearchDaoException;

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

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.apache.commons.lang.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.beans.factory.annotation.Required;

/**
 * The Class MembershipDAOImpl.
 */
public class MembershipDAOImpl extends WhichDoctorBaseDAOImpl implements MembershipDAO {

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

    /** The Constant MAX_OBJECTTYPES. */
    private static final int MAX_OBJECTTYPES = 5;

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

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

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

    /** The membership hash. */
    private Map<String, MembershipBean> membershipHash;

    /** The training limits. */
    private Properties trainingLimits;

    /** The default membership class. */
    private String defaultMembershipClass;

    /** The default membership type. */
    private String defaultMembershipType;

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

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

    /** The preferences dao. */
    @Resource
    private PreferencesDAO preferencesDAO;

    /** The privileges dao. */
    @Resource
    private PrivilegesDAO privilegesDAO;

    /** The specialty dao. */
    @Resource
    private SpecialtyDAO specialtyDAO;

    /** The field map dao. */
    @Resource
    private FieldMapDAO fieldMapDAO;

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

    /** The Constant NOT_IN_TRAINING. */
    private final static int NOT_IN_TRAINING = 1;

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

    /** The Constant STANDARD_POST_FRACP. */
    private final static int STANDARD_POST_FRACP = 3;

    /** The Constant STANDARD_NO_CURRENT. */
    private final static int STANDARD_NO_CURRENT = 4;

    /** The Constant CONDITIONAL_UNMET. */
    private final static int CONDITIONAL_UNMET = 5;

    /** The Constant CONDITIONAL_REQUIRES_UPGRADE. */
    private final static int CONDITIONAL_REQUIRES_UPGRADE = 6;

    /** The Constant CONDITIONAL_NO_CURRENT. */
    private final static int CONDITIONAL_NO_CURRENT = 7;

    /** The Constant CONDITIONAL_CONTINUING. */
    private final static int CONDITIONAL_CONTINUING = 8;

    /**
     * Sets the training limits.
     *
     * @param trainingLimitsVal the new training limits
     */
    @Required
    public final void setTrainingLimits(final Properties trainingLimitsVal) {
        this.trainingLimits = trainingLimitsVal;
    }

    /**
     * Gets the training types.
     *
     * @return the training types
     */
    public final Collection<String> getTrainingTypes() {
        Collection<String> types = new ArrayList<String>();

        if (this.trainingLimits != null) {
            for (Object key : this.trainingLimits.keySet()) {
                types.add((String) key);
            }
        }
        return types;
    }

    /**
     * Gets the training limit.
     *
     * @param type the type
     *
     * @return the training limit
     */
    public final int getTrainingLimit(final String type) {
        int value = 0;
        if (this.trainingLimits != null) {
            if (this.trainingLimits.containsKey(type)) {
                String strValue = this.trainingLimits.getProperty(type);
                try {
                    value = Integer.parseInt(strValue);
                } catch (NumberFormatException nfe) {
                    dataLogger.debug("Error parsing integer: " + nfe.getMessage());
                }
            }
        }
        return value;
    }

    /**
     * Sets the default membership class.
     *
     * @param defaultMembershipClassVal the new default membership class
     */
    @Required
    public final void setDefaultMembershipClass(final String defaultMembershipClassVal) {
        this.defaultMembershipClass = defaultMembershipClassVal;
    }

    /**
     * Gets the default membership class.
     *
     * @return the default membership class
     */
    public final String getDefaultMembershipClass() {
        return this.defaultMembershipClass;
    }

    /**
     * Sets the default membership type.
     *
     * @param defaultMembershipTypeVal the new default membership type
     */
    @Required
    public final void setDefaultMembershipType(final String defaultMembershipTypeVal) {
        this.defaultMembershipType = defaultMembershipTypeVal;
    }

    /**
     * Gets the default membership type.
     *
     * @return the default membership type
     */
    public final String getDefaultMembershipType() {
        return this.defaultMembershipType;
    }

    /**
     * Gets the all instances.
     *
     * @return the all instances
     */
    public final Collection<MembershipBean> getAllInstances() {
        if (this.membershipHash == null) {
            loadMembershipHash();
        }
        final Collection<MembershipBean> memberships = new ArrayList<MembershipBean>();
        memberships.addAll(this.membershipHash.values());

        return memberships;
    }

    /**
     * Gets the default instance.
     *
     * @return the default instance
     */
    public final MembershipBean getDefaultInstance() {
        return this.getInstance(this.defaultMembershipClass, this.defaultMembershipType);
    }

    /**
     * Gets the single instance of MembershipDAO.
     *
     * @param membershipClass the membership class
     * @param membershipType the membership type
     *
     * @return single instance of MembershipDAO
     */
    public final MembershipBean getInstance(final String membershipClass, final String membershipType) {
        MembershipBean membership = null;

        if (this.membershipHash == null) {
            loadMembershipHash();
        }
        final String key = membershipClass + "__" + membershipType;
        if (this.membershipHash != null) {
            if (this.membershipHash.containsKey(key)) {
                membership = this.membershipHash.get(key).clone();
            }
        }
        if (membership == null) {
            membership = new MembershipBean();
        }
        return membership;
    }

    /**
     * Used to get an ArrayList of MembershipBean details for a specified GUID
     * number.
     *
     * @param guid the guid
     * @param allMembership the all membership
     * @param membershipClass the membership class
     * @param membershipType the membership type
     *
     * @return the collection< membership bean>
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    @SuppressWarnings("unchecked")
    public final Collection<MembershipBean> load(final int guid, final boolean allMembership,
            final String membershipClass, final String membershipType) throws WhichDoctorDaoException {

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

        // Do check whether required results are light (only primary)
        // or full (all memberships)
        boolean specificMembership = false;

        StringBuffer sql = new StringBuffer();
        sql.append(getSQL().getValue("membership/load"));
        sql.append(" WHERE membership.Active = true" + " AND membership.ReferenceGUID = ?");

        Collection<Object> parameters = new ArrayList<Object>();
        parameters.add(guid);

        if (StringUtils.isNotBlank(membershipClass)) {
            if (!StringUtils.equals(membershipClass, "Preferred")) {
                sql.append(" AND membershiptype.Class = ?");
                parameters.add(membershipClass);
                specificMembership = true;
            }
        }
        if (StringUtils.isNotBlank(membershipType)) {
            if (!StringUtils.equals(membershipType, "Preferred")) {
                sql.append(" AND membershiptype.Name = ?");
                parameters.add(membershipType);
                specificMembership = true;
            }
        }
        sql.append(" ORDER BY PrimaryMembership DESC, membership.GUID ASC");
        if (!allMembership) {
            sql.append(" LIMIT 1");
        }

        Collection<MembershipBean> memberships = new ArrayList<MembershipBean>();
        try {
            memberships = this.getJdbcTemplateReader().query(sql.toString(), parameters.toArray(), new RowMapper() {
                public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                    return loadMembership(rs);
                }
            });

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

        if (specificMembership) {
            // Specific membership field set, check to see if result
            // If not load more generic
            if (memberships.size() == 0) {
                if (StringUtils.isNotBlank(membershipType)) {
                    /* MembershipType set, load just membership class */
                    memberships = load(guid, allMembership, membershipClass, null);
                }
                if (memberships.size() == 0) {
                    if (StringUtils.isNotBlank(membershipClass)) {
                        /* MembershipClass set, load just membership */
                        memberships = load(guid, allMembership, null, null);
                    }
                }
            }
        }
        return memberships;
    }

    /**
     * Load the MembershipBean based on the supplied membership id.
     *
     * @param membershipId the membership id
     *
     * @return the membership bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final MembershipBean load(final int membershipId) throws WhichDoctorDaoException {

        dataLogger.info("Getting membershipId:" + membershipId);

        final String loadSQL = getSQL().getValue("membership/load") + " WHERE membership.Id = ?";

        MembershipBean membership = null;
        try {
            membership = (MembershipBean) this.getJdbcTemplateReader().queryForObject(loadSQL,
                    new Object[] { membershipId }, new RowMapper() {
                        public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                            return loadMembership(rs);
                        }
                    });

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

    /**
     * Creates the MembershipBean.
     *
     * @param membership the membership
     * @param checkUser the check user
     * @param privileges the privileges
     *
     * @return the int
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final int create(final MembershipBean membership, final UserBean checkUser,
            final PrivilegesBean privileges) throws WhichDoctorDaoException {
        membership.setActive(true);
        return save(membership, checkUser, privileges, "create");
    }

    /**
     * Modify the MembershipBean.
     *
     * @param membership the membership
     * @param checkUser the check user
     * @param privileges the privileges
     *
     * @return the int
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final int modify(final MembershipBean membership, final UserBean checkUser,
            final PrivilegesBean privileges) throws WhichDoctorDaoException {
        membership.setActive(true);
        return save(membership, checkUser, privileges, "modify");
    }

    /**
     * Delete the MembershipBean.
     *
     * @param membership the membership
     * @param checkUser the check user
     * @param privileges the privileges
     *
     * @return true, if successful
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final boolean delete(final MembershipBean membership, final UserBean checkUser,
            final PrivilegesBean privileges) throws WhichDoctorDaoException {
        boolean success = false;

        membership.setActive(false);
        int deleted = save(membership, checkUser, privileges, "delete");
        if (deleted > 0) {
            success = true;
        }
        return success;
    }

    /**
     * Save the updated MembershipBean.
     *
     * @param membership the membership
     * @param checkUser the check user
     * @param privileges the privileges
     * @param action the action
     *
     * @return the int
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private int save(final MembershipBean membership, final UserBean checkUser, final PrivilegesBean privileges,
            final String action) throws WhichDoctorDaoException {

        /* Check required information within membership bean is present */
        if (membership.getMembershipClass() == null) {
            throw new WhichDoctorDaoException("Membership class cannot be null");
        }
        if (StringUtils.isBlank(membership.getMembershipClass())) {
            throw new WhichDoctorDaoException("Membership class cannot be an empty string");
        }
        if (membership.getMembershipType() == null) {
            throw new WhichDoctorDaoException("Membership type cannot be null");
        }
        if (membership.getReferenceGUID() == 0) {
            throw new WhichDoctorDaoException("Membership requires a valid Reference GUID number");
        }
        if (!privileges.getPrivilege(checkUser, "membership", action)) {
            throw new WhichDoctorDaoException("Insufficient user credentials to " + action + " membership entry");
        }

        int membershipTypeId = 0;
        try {
            ObjectTypeBean object = this.getObjectTypeDAO().load("Membership", membership.getMembershipType(),
                    membership.getMembershipClass());
            membershipTypeId = object.getObjectTypeId();
        } catch (Exception e) {
            dataLogger.error("Error loading objecttype for membership type: " + e.getMessage());
        }
        if (membershipTypeId == 0) {
            throw new WhichDoctorDaoException("Membership type not found");
        }

        int membershipId = 0;

        /* Load objecttypeIds */
        HashMap<Integer, Integer> objectTypeIds = new HashMap<Integer, Integer>();

        for (int i = 1; i <= MAX_OBJECTTYPES; i++) {
            /* By default set the objectTypeId value to 0 */
            objectTypeIds.put(new Integer(i), new Integer(0));
            if (membership.getObjectTypeField(i) != null) {
                try {
                    ObjectTypeBean object = membership.getObjectTypeField(i);
                    ObjectTypeBean loadedObject = this.getObjectTypeDAO().load(object.getObject(), object.getName(),
                            object.getClassName());
                    objectTypeIds.put(new Integer(i), new Integer(loadedObject.getObjectTypeId()));
                } catch (Exception e) {
                    dataLogger.error("Error loading objecttype Id: " + e.getMessage());
                }
            }
        }

        ArrayList<Object> parameters = new ArrayList<Object>();
        parameters.add(membership.getReferenceGUID());
        parameters.add(membershipTypeId);
        parameters.add(membership.getMemo());
        parameters.add(membership.getJoinedDate());
        parameters.add(membership.getLeftDate());
        parameters.add(membership.getPrimary());

        /* Set integer fields */
        for (int i = 1; i <= MAX_INTEGERS; i++) {
            parameters.add(membership.getIntField(i));
        }

        /* Set objecttype fields */
        for (int i = 1; i <= MAX_OBJECTTYPES; i++) {
            parameters.add(objectTypeIds.get(i));
        }

        /* Set String fields */
        for (int i = 1; i <= MAX_CHARS; i++) {
            parameters.add(membership.getCharField(i));
        }

        /* Set date fields */
        for (int i = 1; i <= MAX_DATES; i++) {
            Date membershipDate = null;

            if (membership.getDateField(i) != null) {
                membershipDate = new Date(membership.getDateField(i).getTime());
            }
            parameters.add(membershipDate);
        }

        /* The general details about this membership field */
        Timestamp sqlTimeStamp = new Timestamp(Calendar.getInstance().getTimeInMillis());
        parameters.add(membership.getActive());
        parameters.add(sqlTimeStamp);
        parameters.add(checkUser.getDN());
        parameters.add(membership.getLogMessage(action));

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

        try {
            Integer[] result = this.performUpdate("membership", membership.getGUID(), parameters, "Membership",
                    checkUser, action);
            /* Set the returned guid and id values */
            membership.setGUID(result[0]);
            membershipId = result[1];
        } catch (Exception e) {
            dataLogger.error("Error processing membership record: " + e.getMessage());
            throw new WhichDoctorDaoException("Error processing membership record: " + e.getMessage());
        }

        if (membershipId > 0) {
            /* Update primary flag to reflect modification */
            updatePrimary(membership, action);

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

        }
        return membershipId;
    }

    /**
     * Load membership hash.
     */
    private void loadMembershipHash() {
        this.membershipHash = new HashMap<String, MembershipBean>();

        // Load all the membership object types
        Collection<ObjectTypeBean> objects = null;
        try {
            objects = this.getObjectTypeDAO().load("Membership");
        } catch (SFSDaoException sde) {
            dataLogger.error("Error loading Membership object types: " + sde.getMessage());
        }
        if (objects != null) {
            for (ObjectTypeBean object : objects) {
                MembershipBean membership = null;

                final String strClass = object.getClassName();
                final String strType = object.getName();
                final String key = strClass + "__" + strType;

                dataLogger.debug("Membership Key: " + key);

                try {
                    Map<String, FieldMapBean> fieldMapping = this.fieldMapDAO.load(strClass, strType);

                    membership = new MembershipBean();
                    membership.setType(strClass, strType, fieldMapping);
                } catch (Exception e) {
                    dataLogger.error("Error initiating new MembershipBean: " + e.getMessage());
                }
                if (membership != null) {
                    // Add the membership bean to the internal hash
                    this.membershipHash.put(key, membership);
                }
            }
        }
    }

    /**
     * Load membership.
     *
     * @param rs the rs
     *
     * @return the membership bean
     *
     * @throws SQLException the SQL exception
     */
    private MembershipBean loadMembership(final ResultSet rs) throws SQLException {

        MembershipBean membership = this.getInstance(rs.getString("MembershipClass"),
                rs.getString("MembershipType"));

        membership.setId(rs.getInt("Id"));
        membership.setGUID(rs.getInt("GUID"));
        membership.setReferenceGUID(rs.getInt("ReferenceGUID"));
        membership.setMemo(rs.getString("Memo"));

        try {
            membership.setJoinedDate(rs.getDate("JoinedDate"));
        } catch (SQLException sqe) {
            dataLogger.debug("Error reading JoinedDate: " + sqe.getMessage());
        }

        try {
            membership.setLeftDate(rs.getDate("LeftDate"));
        } catch (SQLException sqe) {
            dataLogger.debug("Error reading LeftDate: " + sqe.getMessage());
        }

        membership.setPrimary(rs.getBoolean("PrimaryMembership"));

        for (int i = 1; i <= MAX_INTEGERS; i++) {
            String field = "intField" + i;
            membership.setIntField(i, rs.getInt(field));
        }

        for (int i = 1; i <= MAX_OBJECTTYPES; i++) {
            String field = "objecttypeField" + i;
            if (rs.getInt(field) > 0) {
                // Value exists, load objecttype bean from supplied dataset
                ObjectTypeBean objectType = loadObjectType(String.valueOf(i), rs);
                membership.setObjectTypeField(i, objectType);
            }
        }

        for (int i = 1; i <= MAX_CHARS; i++) {
            String field = "charField" + i;
            membership.setCharField(i, rs.getString(field));
        }

        for (int i = 1; i <= MAX_DATES; i++) {
            String field = "dateField" + i;
            try {
                membership.setDateField(i, rs.getDate(field));
            } catch (SQLException sqe) {
                dataLogger.debug("Error reading dateField (" + i + "): " + sqe.getMessage());
            }
        }

        membership.setActive(rs.getBoolean("Active"));
        try {
            membership.setCreatedDate(rs.getTimestamp("CreatedDate"));
        } catch (SQLException sqe) {
            dataLogger.debug("Error reading CreatedDate: " + sqe.getMessage());
        }
        membership.setCreatedBy(rs.getString("CreatedBy"));
        try {
            membership.setModifiedDate(rs.getTimestamp("ModifiedDate"));
        } catch (SQLException sqe) {
            dataLogger.debug("Error reading ModifiedDate: " + sqe.getMessage());
        }
        membership.setModifiedBy(rs.getString("ModifiedBy"));
        try {
            membership.setExportedDate(rs.getTimestamp("ExportedDate"));
        } catch (SQLException sqe) {
            dataLogger.debug("Error reading ExportedDate: " + sqe.getMessage());
        }
        membership.setExportedBy(rs.getString("ExportedBy"));

        return membership;
    }

    /**
     * Load object type.
     *
     * @param fieldValue the field value
     * @param rs the rs
     *
     * @return the object type bean
     */
    private ObjectTypeBean loadObjectType(final String fieldValue, final ResultSet rs) {
        // Load objecttype bean from supplied dataset
        ObjectTypeBean objectType = new ObjectTypeBean();
        try {
            objectType.setObjectTypeId(rs.getInt("objecttypeField" + fieldValue));
            objectType.setName(rs.getString("objecttypeName" + fieldValue));
            objectType.setClassName(rs.getString("objecttypeClass" + fieldValue));
            objectType.setObject(rs.getString("objecttypeObject" + fieldValue));
            objectType.setAbbreviation(rs.getString("objecttypeAbbreviation" + fieldValue));
            objectType.setSecurity(rs.getString("objecttypeSecurity" + fieldValue));
            objectType.setLdapMapping(1, rs.getString("objecttypeLdapMapping" + fieldValue));
        } catch (Exception e) {
            dataLogger.error("Error loading objecttype data");
            return null;
        }
        return objectType;
    }

    /**
     * If the MembershipBean is primary ensure no other primary entry exists for
     * the ReferenceGUID.
     *
     * @param membership the membership
     * @param action the action
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private void updatePrimary(final MembershipBean membership, final String action)
            throws WhichDoctorDaoException {

        if (membership.getPrimary()) {
            if (!StringUtils.equals(action, "delete")) {
                /* Find old primary address and turn off flag */
                this.getJdbcTemplateWriter().update(this.getSQL().getValue("membership/updatePrimary"),
                        new Object[] { false, membership.getReferenceGUID(), membership.getGUID(), true });

            }
        }
    }

    /**
     * Gets the candidate number.
     *
     * @param personGUID the person guid
     *
     * @return the candidate number
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final String getCandidateNumber(final int personGUID) throws WhichDoctorDaoException {
        String candidateNo = "";
        String year = "";

        /* By default use the current year */
        Date examDate = new Date(Calendar.getInstance().getTimeInMillis());

        if (personGUID > 0) {
            // Get year, use cutoff date of Nov 22 for year.
            // 23 Nov is next year
            BuilderBean builderBean = new BuilderBean();
            builderBean.setParameter("EXAMS", true);
            PersonBean person = this.personDAO.loadGUID(personGUID, builderBean);

            boolean writtenExamSet = false;
            if (person != null && person.getExams() != null) {
                for (ExamBean exam : person.getExams()) {
                    // Assume that exams are returned in chronological
                    // order, first written exam is the assumed year.
                    if (!writtenExamSet) {
                        if (StringUtils.equals(exam.getType(), "Written Exam")) {
                            examDate = new Date(exam.getDateSat().getTime());
                            writtenExamSet = true;
                        }
                    }
                }
            }
        }
        /* Format the year */
        SimpleDateFormat df = new SimpleDateFormat("yyyy");
        year = df.format(examDate);

        final int yearLength = 4;
        final int randomNumberLength = 4;
        final int randomSize = 10;

        candidateNo += year.substring(0, 1) + year.substring(2, yearLength);
        /* Generate four digit random number */
        Random rand = new Random();
        for (int x = 0; x < randomNumberLength; x++) {
            int random = rand.nextInt(randomSize);
            candidateNo += String.valueOf(random);
        }
        if (!uniqueCandidateNumber(personGUID, candidateNo)) {
            candidateNo = getCandidateNumber(personGUID);
        }
        return candidateNo;
    }

    /**
     * Validate candidate number.
     *
     * @param personGUID the person guid
     * @param testNumber the test number
     *
     * @return the string
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final String validateCandidateNumber(final int personGUID, final int testNumber)
            throws WhichDoctorDaoException {

        String candidateNumber = String.valueOf(testNumber);

        final int candidateNumberLength = 7;

        if (candidateNumber.length() != candidateNumberLength) {
            // Candidate number length is invalid
            // Generate a new unique number
            candidateNumber = getCandidateNumber(personGUID);
        }
        /* Test uniqueness */
        if (!uniqueCandidateNumber(personGUID, candidateNumber)) {
            candidateNumber = getCandidateNumber(personGUID);
        }
        return candidateNumber;
    }

    /**
     * Check if the same number exists already. If it does then generate a new
     * number
     *
     * @param personGUID the person guid
     * @param candidateNumber the candidate number
     *
     * @return true, if unique candidate number
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private boolean uniqueCandidateNumber(final int personGUID, final String candidateNumber)
            throws WhichDoctorDaoException {

        boolean unique = true;

        try {
            int uniquenessCount = this.getJdbcTemplateReader().queryForInt(
                    this.getSQL().getValue("membership/checkCandidateNo"),
                    new Object[] { personGUID, candidateNumber });

            if (uniquenessCount > 0) {
                /* The number is not unique */
                unique = false;
            }
        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }
        return unique;
    }

    /**
     * Automatically updates the membership status of a person if their details
     * meet the criteria.
     *
     * The training status can be one of five possible types:
     * - "Not in training"
     * - "Training" - "Standard"
     * - "Training" - "Post-FRACP"
     * - "Training" - "No current rotation"
     *   The person is a trainee but is not currently undertaking a rotation
     * - "Conditional training" - "Unmet requirements"
     *   Undertaking AT rotations but hasn't met BT requirements
     * - "Conditional training" - "Requires upgrade"
     *   Has met BT requirements but has yet to be updated to an AT
     *
     * @param personGUID the person guid
     * @param user the user
     * @param privileges the privileges
     */
    public final void updateTrainingStatus(final int personGUID, final UserBean user,
            final PrivilegesBean privileges) {

        boolean membershipUpgrade = false;

        // Get the preferences and check whether the automatic update should be performed.
        try {
            final PreferencesBean preferences = this.preferencesDAO.load();
            final String update = preferences.getOption("system", "membershipupgrade");
            if (StringUtils.isNotBlank(update)) {
                membershipUpgrade = Boolean.parseBoolean(update);
            }
        } catch (WhichDoctorDaoException wde) {
            dataLogger.error("Error loading preferences: " + wde.getMessage());
        }

        dataLogger.info("Performing automatic update check: " + personGUID);

        // Load the person based on the guid with
        // basic training summary and exams
        PersonBean person = new PersonBean();
        try {
            BuilderBean loadDetails = new BuilderBean();
            loadDetails.setParameter("EXAMS", true);
            loadDetails.setParameter("MEMBERSHIP", true);
            loadDetails.setParameter("TRAINING_BASIC", true);
            loadDetails.setParameter("TRAINING_ROTATIONS", true);

            person = this.personDAO.loadGUID(personGUID, loadDetails);

        } catch (Exception e) {
            dataLogger.error("Error loading person for automated training update: " + e.getMessage());
        }
        dataLogger.info("Performing training status update for: " + person.getGUID());

        int trainingStatusId = NOT_IN_TRAINING;

        if (StringUtils.equalsIgnoreCase(person.getMembershipField("Membership Type"), "Member: Basic Trainee")) {
            // The person is a basic trainee, check if undertaking a rotation
            trainingStatusId = STANDARD_NO_CURRENT;

            if (hasCurrentRotation(person)) {
                trainingStatusId = STANDARD_TRAINING;
            }

            if (testIfMembershipUpgradeApplicable(person)) {
                if (membershipUpgrade) {
                    dataLogger.info("Performing automated membership upgrade");
                    changeToAdvancedTrainee(personGUID, user, privileges);
                } else {
                    // By default set to conditional - no current
                    trainingStatusId = CONDITIONAL_NO_CURRENT;
                    if (hasCurrentRotation(person)) {
                        // Is currently training with some sort of rotation
                        trainingStatusId = CONDITIONAL_CONTINUING;
                    }
                    if (hasCurrentRotation(person, "Advanced Training")) {
                        // Is currently training with an advanced rotation
                        trainingStatusId = CONDITIONAL_REQUIRES_UPGRADE;
                    }
                }
            } else {
                // Check if they are currently undertaking any AT rotations
                if (hasCurrentRotation(person, "Advanced Training")) {
                    trainingStatusId = CONDITIONAL_UNMET;
                }
            }
        }

        if (StringUtils.equalsIgnoreCase(person.getMembershipField("Membership Type"),
                "Member: Advanced Trainee")) {
            // The person is an advanced trainee, check if undertaking a rotation
            trainingStatusId = STANDARD_NO_CURRENT;

            if (hasCurrentRotation(person)) {
                trainingStatusId = STANDARD_TRAINING;
            }
        }

        if (trainingStatusId == NOT_IN_TRAINING) {
            // Check if currently undertaking a rotation.
            if (hasCurrentRotation(person)) {
                trainingStatusId = STANDARD_POST_FRACP;
            }
        }

        try {
            updateTrainingStatus(person, trainingStatusId);
        } catch (WhichDoctorDaoException wde) {
            dataLogger.error("Error updating training status: " + wde.getMessage());
        }
    }

    /**
     * Updates all training statuses.
     *
     * @param user the user
     * @param privileges the privileges
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final void updateAllTrainingStatuses(final UserBean user, final PrivilegesBean privileges)
            throws WhichDoctorDaoException {

        dataLogger.info("Rebuilding all training statuses");

        Collection<PersonBean> people = personDAO.loadActivePeople();

        for (PersonBean person : people) {
            updateTrainingStatus(person.getGUID(), user, privileges);
            dataLogger.info("Refreshed training status for guid: " + person.getGUID());
        }
    }

    /**
     * Update training statuses between two dates.
     *
     * @param startDate the start date
     * @param endDate the end date
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final void updateTrainingStatuses(final java.util.Date startDate, final java.util.Date endDate)
            throws WhichDoctorDaoException {

        dataLogger.info("Rebuilding training statuses between two dates");

        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);

            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);

            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);
                    }
                }
            }
        } 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);

            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);

            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);
                    }
                }
            }
        } catch (WhichDoctorSearchDaoException wse) {
            dataLogger.error("Error performing search for recently started " + "rotations: " + wse.getMessage());
        }

        UserBean systemUser = getSystemUser("Membership", "System");

        PrivilegesBean privileges = new PrivilegesBean();
        try {
            privileges = this.privilegesDAO.load();
        } catch (SFSDaoException sfe) {
            dataLogger.error("Error loading privileges: " + sfe.getMessage());
        }

        // With the searches complete iterate through the map and rebuild each
        // of the training statuses.
        for (Integer personGUID : peopleGUIDs.keySet()) {
            dataLogger.info("Rebuilding training status for person GUID: " + personGUID);
            this.updateTrainingStatus(personGUID, systemUser, privileges);
        }
    }

    /**
     * Update the training status.
     *
     * @param person the person
     * @param trainingStatusId the training status id
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private void updateTrainingStatus(final PersonBean person, final int trainingStatusId)
            throws WhichDoctorDaoException {

        String trainingStatus = "";
        String trainingStatusDetail = "";

        switch (trainingStatusId) {
        case NOT_IN_TRAINING:
            trainingStatus = "Not in training";
            break;
        case STANDARD_TRAINING:
            trainingStatus = "Training";
            trainingStatusDetail = "Standard";
            break;
        case STANDARD_NO_CURRENT:
            trainingStatus = "Training";
            trainingStatusDetail = "No current rotation";
            break;
        case STANDARD_POST_FRACP:
            trainingStatus = "Training";
            trainingStatusDetail = "Post-FRACP";
            break;
        case CONDITIONAL_REQUIRES_UPGRADE:
            trainingStatus = "Conditional training";
            trainingStatusDetail = "Requires upgrade";
            break;
        case CONDITIONAL_UNMET:
            trainingStatus = "Conditional training";
            trainingStatusDetail = "Unmet requirements";
            break;
        case CONDITIONAL_NO_CURRENT:
            trainingStatus = "Conditional training";
            trainingStatusDetail = "No current rotation";
            break;
        case CONDITIONAL_CONTINUING:
            trainingStatus = "Conditional training";
            trainingStatusDetail = "Continuing training";
            break;
        }

        int objectTypeId = 0;
        try {
            ObjectTypeBean object = this.getObjectTypeDAO().load("Training Status", trainingStatusDetail,
                    trainingStatus);
            objectTypeId = object.getObjectTypeId();
        } catch (SFSDaoException sfe) {
            dataLogger.error("Error loading objecttype for the training status: " + sfe.getMessage());
        }

        dataLogger.info("Training status update for GUID: " + person.getGUID());
        dataLogger.info("Training status: " + trainingStatus);
        dataLogger.info("Training status detail: " + trainingStatusDetail);

        boolean updateTrainingStatus = false;

        if (!StringUtils.equalsIgnoreCase(person.getTrainingStatus(), trainingStatus)) {
            updateTrainingStatus = true;
        }
        if (!StringUtils.equalsIgnoreCase(person.getTrainingStatusDetail(), trainingStatusDetail)) {
            updateTrainingStatus = true;
        }

        if (updateTrainingStatus) {

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

            int updateCount = 0;

            try {
                updateCount = this.getJdbcTemplateWriter().update(
                        this.getSQL().getValue("person/updateTrainingStatus"),
                        new Object[] { objectTypeId, person.getGUID() });

            } catch (DataAccessException de) {
                dataLogger.error("Error updating the training status id for person guid: " + person.getGUID() + ", "
                        + de.getMessage());
            }
            if (updateCount > 0) {
                /* Commit the ISB transaction */
                this.isbTransactionDAO.commit(isbTransaction);
            }
        }
    }

    /**
     * Test if the person is applicable for a membership upgrade.
     *
     * @param person the person
     * @return true, if successful
     */
    private boolean testIfMembershipUpgradeApplicable(final PersonBean person) {

        boolean membershipUpgradeApplicable = false;

        dataLogger.info("Automated training check: person is a basic trainee");

        boolean passedWritten = false;
        boolean passedClinical = false;
        int totalTraining = 0;

        if (person.getExams() != null) {
            for (ExamBean exam : person.getExams()) {
                if (StringUtils.equalsIgnoreCase(exam.getStatus(), "Passed")) {
                    if (StringUtils.equalsIgnoreCase(exam.getType(), "Written Exam")) {
                        passedWritten = true;
                    }
                    if (StringUtils.equalsIgnoreCase(exam.getType(), "Clinical Exam")) {
                        passedClinical = true;
                    }
                }
            }
        }

        final TreeMap<String, AccreditationBean[]> summary = person.getTrainingSummary("Basic Training");

        if (summary != null) {
            for (String key : summary.keySet()) {
                AccreditationBean[] details = summary.get(key);
                AccreditationBean core = details[0];
                AccreditationBean nonCore = details[1];

                totalTraining += core.getWeeksCertified();
                totalTraining += nonCore.getWeeksCertified();
            }
        }

        final int basicTrainingLimit = this.getTrainingLimit("Basic Training");

        if (totalTraining >= basicTrainingLimit && passedWritten && passedClinical) {
            membershipUpgradeApplicable = true;
        }

        return membershipUpgradeApplicable;
    }

    /**
     * Checks if the person has a current rotation.
     *
     * @param person the person
     * @return true, if successful
     */
    private boolean hasCurrentRotation(final PersonBean person) {

        return hasCurrentRotation(person, "");
    }

    /**
     * Checks if the person has a current rotation.
     * If the type parameter is supplied the function checks by type, otherwise ignored.
     *
     * @param person the person
     * @param type the rotation type
     * @return true, if successful
     */
    private boolean hasCurrentRotation(final PersonBean person, final String type) {

        boolean currentRotation = false;

        java.util.Date today = Calendar.getInstance().getTime();

        if (person.getRotations() != null) {
            for (RotationBean rtn : person.getRotations()) {

                if (rtn.getStartDate().compareTo(today) <= 0 && rtn.getEndDate().compareTo(today) >= 0) {
                    if (StringUtils.isNotBlank(type)) {
                        if (StringUtils.equalsIgnoreCase(rtn.getRotationType(), type)) {
                            currentRotation = true;
                        }
                    } else {
                        currentRotation = true;
                    }
                }
            }
        }
        return currentRotation;
    }

    /**
     * Change to advanced trainee.
     *
     * @param guid the guid
     * @param user the user
     * @param privileges the privileges
     */
    private void changeToAdvancedTrainee(final int guid, final UserBean user, final PrivilegesBean privileges) {

        /* The person needs upgrading to an advanced trainee */
        try {
            Collection<MembershipBean> memberships = load(guid, false, "", "");
            if (memberships != null) {
                for (MembershipBean membership : memberships) {

                    if (membership.getPrimary()) {
                        ObjectTypeBean object = new ObjectTypeBean();
                        object.setObject("Membership Type");
                        object.setClassName("Member");
                        object.setName("Advanced Trainee");
                        membership.setField("Membership Type", object);

                        membership.setLogMessage("Advanced trainee automated upgrade");
                        modify(membership, user, privileges);
                    }
                }
            }

        } catch (WhichDoctorDaoException wde) {
            dataLogger.error("Error performing automated membership update: " + wde.getMessage());
        }

        /* Check their specialties to see if they need upgrading */
        try {
            Collection<SpecialtyBean> specialties = this.specialtyDAO.load(guid, true);

            if (specialties != null) {
                for (SpecialtyBean specialty : specialties) {
                    if (StringUtils.equalsIgnoreCase("Basic Training", specialty.getTrainingProgram())
                            && StringUtils.equalsIgnoreCase("In training", specialty.getStatus())) {
                        specialty.setStatus("Completed training");
                        specialty.setLogMessage("Advanced trainee automated upgrade);");

                        specialtyDAO.modify(specialty, user, privileges);
                    }
                }
            }
        } catch (WhichDoctorDaoException wde) {
            dataLogger.error("Error performing automated specialty update: " + wde.getMessage());
        }
    }
}