com.sfs.whichdoctor.isb.publisher.PersonIsbXmlWriter.java Source code

Java tutorial

Introduction

Here is the source code for com.sfs.whichdoctor.isb.publisher.PersonIsbXmlWriter.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.isb.publisher;

import com.sfs.Formatter;
import com.sfs.beans.ObjectTypeBean;
import com.sfs.whichdoctor.beans.AddressBean;
import com.sfs.whichdoctor.beans.EmailBean;
import com.sfs.whichdoctor.beans.ItemBean;
import com.sfs.whichdoctor.beans.GroupBean;
import com.sfs.whichdoctor.beans.MembershipBean;
import com.sfs.whichdoctor.beans.PersonBean;
import com.sfs.whichdoctor.beans.PhoneBean;
import com.sfs.whichdoctor.beans.RelationshipBean;
import com.sfs.whichdoctor.beans.SpecialtyBean;
import com.sfs.whichdoctor.beans.WhichDoctorBean;
import com.sfs.whichdoctor.formatter.PersonFormatter;
import com.sfs.whichdoctor.xml.writer.XmlWriter;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * The Class PersonIsbXmlWriter.
 */
public class PersonIsbXmlWriter {

    /** The action. */
    private String action = "";

    /** The existing person. */
    private PersonBean existingPerson;

    /** The updated person. */
    private PersonBean updatedPerson;

    /** The xmlwriter. */
    private XmlWriter xmlwriter;

    /** The isb logger. */
    private static Logger isbLogger = Logger.getLogger(PersonIsbXmlWriter.class);

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

    /**
     * Gets the action.
     *
     * @return the action
     */
    public final String getAction() {
        return this.action;
    }

    /**
     * Sets the existing person.
     *
     * @param existingPersonRef the new existing person
     */
    public final void setExistingPerson(final PersonBean existingPersonRef) {
        this.existingPerson = existingPersonRef;
    }

    /**
     * Gets the existing person.
     *
     * @return the existing person
     */
    public final PersonBean getExistingPerson() {
        return this.existingPerson;
    }

    /**
     * Sets the updated person.
     *
     * @param updatedPersonRef the new updated person
     */
    public final void setUpdatedPerson(final PersonBean updatedPersonRef) {
        this.updatedPerson = updatedPersonRef;
    }

    /**
     * Gets the updated person.
     *
     * @return the updated person
     */
    public final PersonBean getUpdatedPerson() {
        return this.updatedPerson;
    }

    /**
     * Publish.
     *
     * @param actionVal the action
     *
     * @return the string
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public final String publish(final String actionVal) throws IOException {
        // Test to see whether the required data is available
        if (actionVal == null) {
            throw new IOException("A valid action must be supplied");
        }
        if (StringUtils.equalsIgnoreCase(actionVal, "create")) {
            this.action = "create";
        }
        if (StringUtils.equalsIgnoreCase(actionVal, "modify")) {
            this.action = "modify";
        }
        if (StringUtils.equalsIgnoreCase(actionVal, "delete")) {
            this.action = "delete";
        }

        if (getExistingPerson() == null) {
            throw new IOException("A valid existing person entity is required");
        }
        if (StringUtils.equals(getAction(), "modify")) {
            // Modify requires a valid existing and updated person entity
            if (getUpdatedPerson() == null) {
                throw new IOException("A valid updated person entity is required");
            }
        }

        xmlwriter = new XmlWriter();

        xmlwriter.writeEntity("identity");
        xmlwriter.writeAttribute("id", String.valueOf(getExistingPerson().getPersonIdentifier()));
        if (StringUtils.isNotBlank(getAction())) {
            xmlwriter.writeAttribute("action", getAction());
        }
        writeAttribute("title");
        writeAttribute("gender");
        writeAttribute("preferredName");
        writeAttribute("firstName");
        writeAttribute("middleName");
        writeAttribute("lastName");
        writeAttribute("salutation");
        writeAttribute("curriculumType");
        writeAttribute("curriculumYear");

        if (StringUtils.equalsIgnoreCase(this.getAction(), "create")) {
            // If this is a create action include a generated password
            writeAttribute("password");
        }
        writeRoles();

        writeObjects("emailAddress");
        writeObjects("postalAddress");
        writeObjects("phoneNumber");
        writeObjects("relationship");

        xmlwriter.endEntity();

        return xmlwriter.getXml();
    }

    /**
     * Isb envelope.
     *
     * @param payload the payload
     * @param source the source
     * @param target the target
     *
     * @return the string
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public final String isbEnvelope(final String payload, final String source, final String target)
            throws IOException {

        String xmlPayload = "";
        if (payload != null) {
            if (payload.startsWith("<?xml ")) {
                xmlPayload = StringUtils.replace(payload, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "");
            } else {
                xmlPayload = payload;
            }
        }

        xmlwriter = new XmlWriter();

        xmlwriter.writeEntity("isb");
        xmlwriter.writeAttribute("version", "1.0");
        xmlwriter.writeAttribute("target", target);
        xmlwriter.writeAttribute("source", source);

        xmlwriter.writeXml(xmlPayload);

        xmlwriter.endEntity();

        return xmlwriter.getXml();
    }

    /**
     * Write objects.
     *
     * @param objectType the object type
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeObjects(final String objectType) throws IOException {
        ArrayList<Object> existingObjects = new ArrayList<Object>();
        ArrayList<Object> updatedObjects = new ArrayList<Object>();

        if (getExistingPerson() != null) {
            if (StringUtils.equals(objectType, "emailAddress")) {
                if (getExistingPerson().getEmail() != null) {
                    for (EmailBean email : getExistingPerson().getEmail()) {
                        existingObjects.add(email);
                    }
                }
            }
            if (StringUtils.equals(objectType, "postalAddress")) {
                if (getExistingPerson().getAddress() != null) {
                    for (AddressBean address : getExistingPerson().getAddress()) {
                        existingObjects.add(address);
                    }
                }
            }
            if (StringUtils.equals(objectType, "phoneNumber")) {
                if (getExistingPerson().getPhone() != null) {
                    for (PhoneBean phone : getExistingPerson().getPhone()) {
                        existingObjects.add(phone);
                    }
                }
            }
            if (StringUtils.equals(objectType, "relationship")) {
                if (getExistingPerson().getRelationships() != null) {
                    // For relationships with an ISB map build a custom identifier
                    for (RelationshipBean relationship : getExistingPerson().getRelationships()) {
                        if (StringUtils.isNotBlank(relationship.getISBMapping())) {
                            relationship.setGUID(buildRelationshipId(relationship));
                            existingObjects.add(relationship);
                        }
                    }
                }
            }
        }
        if (getUpdatedPerson() != null) {
            if (StringUtils.equals(objectType, "emailAddress")) {
                if (getUpdatedPerson().getEmail() != null) {
                    for (EmailBean email : getUpdatedPerson().getEmail()) {
                        updatedObjects.add(email);
                    }
                }
            }
            if (StringUtils.equals(objectType, "postalAddress")) {
                if (getUpdatedPerson().getAddress() != null) {
                    for (AddressBean address : getUpdatedPerson().getAddress()) {
                        updatedObjects.add(address);
                    }
                }
            }
            if (StringUtils.equals(objectType, "phoneNumber")) {
                if (getUpdatedPerson().getPhone() != null) {
                    for (PhoneBean phone : getUpdatedPerson().getPhone()) {
                        updatedObjects.add(phone);
                    }
                }
            }
            if (StringUtils.equals(objectType, "relationship")) {
                if (getUpdatedPerson().getRelationships() != null) {
                    // For relationships with an ISB map build a custom identifier
                    for (RelationshipBean relationship : getUpdatedPerson().getRelationships()) {
                        if (StringUtils.isNotBlank(relationship.getISBMapping())) {
                            relationship.setGUID(buildRelationshipId(relationship));
                            updatedObjects.add(relationship);
                        }
                    }
                }
            }
        }
        processObjects(existingObjects, updatedObjects);
    }

    /**
     * Builds the relationship id.
     *
     * @param relationship the relationship
     *
     * @return the int
     */
    private int buildRelationshipId(final RelationshipBean relationship) {
        int guid = 0;
        final String strGuid = String.valueOf(relationship.getHierarchy()) + "00" + relationship.getReferenceGUID();
        try {
            guid = Integer.parseInt(strGuid);
        } catch (NumberFormatException nfe) {
            isbLogger.debug("Error parsing GUID: " + nfe.getMessage());
        }
        return guid;
    }

    /**
     * Compare object.
     *
     * @param existingObject the existing object
     * @param updatedObject the updated object
     *
     * @return true, if successful
     */
    private boolean compareObject(final Object existingObject, final Object updatedObject) {
        boolean different = false;

        String className = "";
        if (existingObject != null) {
            className = existingObject.getClass().getName();
        }
        if (updatedObject != null) {
            className = existingObject.getClass().getName();
        }
        isbLogger.debug("Object class name: " + className);

        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.EmailBean")) {
            EmailBean existing = (EmailBean) existingObject;
            different = existing.compare((EmailBean) updatedObject);
        }
        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.AddressBean")) {
            AddressBean existing = (AddressBean) existingObject;
            different = existing.compare((AddressBean) updatedObject, false);
        }
        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.PhoneBean")) {
            PhoneBean existing = (PhoneBean) existingObject;
            different = existing.compare((PhoneBean) updatedObject);
        }
        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.RelationshipBean")) {
            RelationshipBean existing = (RelationshipBean) existingObject;
            different = existing.compare((RelationshipBean) updatedObject);
        }
        return different;
    }

    /**
     * Write object.
     *
     * @param existingObject the existing object
     * @param updatedObject the updated object
     * @param state the state
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeObject(final Object existingObject, final Object updatedObject, final String state)
            throws IOException {
        String className = "";
        if (existingObject != null) {
            className = existingObject.getClass().getName();
        }
        if (updatedObject != null) {
            className = existingObject.getClass().getName();
        }
        isbLogger.debug("Object class name: " + className);

        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.EmailBean")) {
            writeEmailAddress((EmailBean) existingObject, (EmailBean) updatedObject, state);
        }
        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.AddressBean")) {
            writeAddress((AddressBean) existingObject, (AddressBean) updatedObject, state);
        }
        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.PhoneBean")) {
            writePhoneNumber((PhoneBean) existingObject, (PhoneBean) updatedObject, state);
        }
        if (StringUtils.equals(className, "com.sfs.whichdoctor.beans.RelationshipBean")) {
            writeRelationship((RelationshipBean) existingObject, (RelationshipBean) updatedObject, state);
        }
    }

    /**
     * Write value.
     *
     * @param attribute the attribute
     * @param person the person
     *
     * @return the string
     */
    private String writeValue(final String attribute, final PersonBean person) {
        String value = null;

        try {
            if (StringUtils.equals(attribute, "title")) {
                value = person.getTitle();
            }
            if (StringUtils.equals(attribute, "gender")) {
                value = person.getGender();
            }
            if (StringUtils.equals(attribute, "preferredName")) {
                value = person.getPreferredName();
            }
            if (StringUtils.equals(attribute, "firstName")) {
                value = person.getFirstName();
            }
            if (StringUtils.equals(attribute, "middleName")) {
                value = person.getMiddleName();
            }
            if (StringUtils.equals(attribute, "lastName")) {
                value = person.getLastName();
            }
            if (StringUtils.equals(attribute, "salutation")) {
                value = person.getHonors();
            }
            if (StringUtils.equals(attribute, "curriculumType") && isTrainee(person)) {
                value = person.getMembershipField("Division");
            }
            if (StringUtils.equals(attribute, "curriculumYear")) {
                if (person.getSpecialtyList() != null) {
                    for (SpecialtyBean specialty : person.getSpecialtyList()) {
                        if (StringUtils.equalsIgnoreCase(specialty.getStatus(), "In training")
                                && specialty.getTrainingProgramYear() > 0) {
                            value = String.valueOf(specialty.getTrainingProgramYear());
                        }
                    }
                }
            }
            if (StringUtils.equals(attribute, "password")) {
                value = person.getPassword();
            }
        } catch (Exception e) {
            // Catch any potential exception - just a sign that no value exists
            isbLogger.debug("Error getting ISB XML value for person: " + e.getMessage());
        }
        return value;
    }

    /**
     * The writeEmailAddress and following writeAttribute functions output an
     * email ISB object.
     *
     * @param existingEmail - the existing email object
     * @param updatedEmail - the updated email object
     * @param state - the state of the ISB transaction
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeEmailAddress(final EmailBean existingEmail, final EmailBean updatedEmail, final String state)
            throws IOException {
        xmlwriter.writeEntity("object").writeAttribute("class", "emailAddress");

        writeAttribute("email", state, writeValue("email", existingEmail), writeValue("email", updatedEmail));

        writeAttribute("primary", state, writeValue("primary", existingEmail), writeValue("primary", updatedEmail));

        writeAttribute("type", state, writeValue("type", existingEmail), writeValue("type", updatedEmail));

        xmlwriter.endEntity();
    }

    /**
     * Write value.
     *
     * @param attribute the attribute
     * @param email the email
     *
     * @return the string
     */
    private String writeValue(final String attribute, final EmailBean email) {
        String value = null;

        try {
            if (StringUtils.equals(attribute, "email")) {
                value = email.getEmail();
            }
            if (StringUtils.equals(attribute, "primary")) {
                value = Formatter.convertBoolean(email.getPrimary(), "true", "false");
            }
            if (StringUtils.equals(attribute, "type")) {
                value = email.getContactType();
            }
        } catch (Exception e) {
            // Catch any potential exception - just a sign that no value exists
            isbLogger.debug("Error getting ISB XML value for email: " + e.getMessage());
        }

        return value;
    }

    /**
     * The writePhoneNumber and following writeAttribute functions output an
     * phone ISB object.
     *
     * @param existingPhone - the existing phone object
     * @param updatedPhone - the updated phone object
     * @param state - the state of the ISB transaction
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writePhoneNumber(final PhoneBean existingPhone, final PhoneBean updatedPhone, final String state)
            throws IOException {
        xmlwriter.writeEntity("object").writeAttribute("class", "phoneNumber");

        writeAttribute("number", state, writeValue("number", existingPhone), writeValue("number", updatedPhone));

        writeAttribute("primary", state, writeValue("primary", existingPhone), writeValue("primary", updatedPhone));

        writeAttribute("type", state, writeValue("type", existingPhone), writeValue("type", updatedPhone));

        xmlwriter.endEntity();
    }

    /**
     * Write value.
     *
     * @param attribute the attribute
     * @param phone the phone
     *
     * @return the string
     */
    private String writeValue(final String attribute, final PhoneBean phone) {
        String value = null;

        try {
            if (StringUtils.equals(attribute, "number")) {
                value = Formatter.getNumericPhone(phone.getCountryCode(), phone.getAreaCode(), phone.getNumber());
            }
            if (StringUtils.equals(attribute, "primary")) {
                value = Formatter.convertBoolean(phone.getPrimary(), "true", "false");
            }
            if (StringUtils.equals(attribute, "type")) {
                value = phone.getContactType();
            }
        } catch (Exception e) {
            // Catch any potential exception - just a sign that no value exists
            isbLogger.debug("Error getting ISB XML value for phone: " + e.getMessage());
        }
        return value;
    }

    /**
     * The writeAddress and following writeAttribute functions output an address
     * ISB object.
     *
     * @param existingAddress - the existing address object
     * @param updatedAddress - the updated address object
     * @param state - the state of the ISB transaction
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeAddress(final AddressBean existingAddress, final AddressBean updatedAddress,
            final String state) throws IOException {
        xmlwriter.writeEntity("object").writeAttribute("class", "postalAddress");

        for (int i = 1; i < AddressBean.MAX_FIELD_COUNT; i++) {
            final String field = "addressField" + i;
            writeAttribute(field, state, writeValue(field, existingAddress), writeValue(field, updatedAddress));
        }

        writeAttribute("city", state, writeValue("city", existingAddress), writeValue("city", updatedAddress));

        writeAttribute("state", state, writeValue("state", existingAddress), writeValue("state", updatedAddress));

        writeAttribute("country", state, writeValue("country", existingAddress),
                writeValue("country", updatedAddress));

        writeAttribute("postCode", state, writeValue("postCode", existingAddress),
                writeValue("postCode", updatedAddress));

        writeAttribute("primary", state, writeValue("primary", existingAddress),
                writeValue("primary", updatedAddress));

        writeAttribute("type", state, writeValue("type", existingAddress), writeValue("type", updatedAddress));

        xmlwriter.endEntity();
    }

    /**
     * Write value.
     *
     * @param attribute the attribute
     * @param address the address
     *
     * @return the string
     */
    private String writeValue(final String attribute, final AddressBean address) {
        String value = null;
        try {
            for (int i = 1; i < AddressBean.MAX_FIELD_COUNT; i++) {
                final String field = "addressField" + i;
                if (StringUtils.equals(attribute, field)) {
                    if (address.getAddressFieldCount() > i) {
                        value = address.getAddressField(i - 1);
                    }
                }
            }
            if (StringUtils.equals(attribute, "city")) {
                // Ensure that no commas are present
                value = StringUtils.replace(address.getCity(), ",", "");
            }
            if (StringUtils.equals(attribute, "state")) {
                // Ensure that no commas are present in the output
                value = StringUtils.replace(address.getState(), ",", "");
            }
            if (StringUtils.equals(attribute, "country")) {
                // Ensure that no commas are present in the output
                value = StringUtils.replace(address.getCountryAbbreviation(), ",", "");
            }
            if (StringUtils.equals(attribute, "postCode")) {
                value = address.getPostCode();
            }
            if (StringUtils.equals(attribute, "primary")) {
                value = Formatter.convertBoolean(address.getPrimary(), "true", "false");
            }
            if (StringUtils.equals(attribute, "type")) {
                value = address.getContactClass() + " - " + address.getContactType();
            }
        } catch (Exception e) {
            // Catch any potential exception - just a sign that no value exists
            isbLogger.debug("Error getting ISB XML value for address: " + e.getMessage());
        }

        return value;
    }

    /**
     * Output a relationship ISB object.
     *
     * @param existingRelationship - the existing relationship object
     * @param updatedRelationship - the updated relationship object
     * @param state - the state of the ISB transaction
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeRelationship(final RelationshipBean existingRelationship,
            final RelationshipBean updatedRelationship, final String state) throws IOException {

        xmlwriter.writeEntity("object").writeAttribute("class", "relationship");

        String existingMembershipType = "";
        String updatedMembershipType = "";

        if (this.getExistingPerson() != null) {
            existingMembershipType = PersonFormatter.getField(this.getExistingPerson(), "Membership Type", "",
                    null);
        }

        if (this.getUpdatedPerson() != null) {
            updatedMembershipType = PersonFormatter.getField(this.getUpdatedPerson(), "Membership Type", "", null);
        }

        writeAttribute("uniqueId", state, writeValue("uniqueId", existingRelationship, existingMembershipType),
                writeValue("uniqueId", updatedRelationship, updatedMembershipType));

        writeAttribute("type", state, writeValue("type", existingRelationship, existingMembershipType),
                writeValue("type", updatedRelationship, updatedMembershipType));

        xmlwriter.endEntity();
    }

    /**
     * Write value.
     *
     * @param attribute the attribute
     * @param relationship the relationship
     * @param membershipType the membership type
     *
     * @return the string
     */
    private String writeValue(final String attribute, final RelationshipBean relationship,
            final String membershipType) {
        String value = null;

        try {
            if (StringUtils.equals(attribute, "uniqueId")) {
                value = relationship.getIdentifier();
            }
            if (StringUtils.equals(attribute, "type")) {
                if (StringUtils.isNotBlank(relationship.getISBMapping())) {
                    String prefix = "";
                    if (relationship.getEndDate() != null) {
                        prefix = df.format(relationship.getEndDate()) + " ";
                    }
                    value = prefix + relationship.getISBMapping().trim();
                }
            }
        } catch (Exception e) {
            // Catch any potential exception - just a sign that no value exists
            isbLogger.debug("Error getting ISB XML value for relationship: " + e.getMessage());
        }

        return value;
    }

    // These are helper methods that do not need modifcation
    // to support further attributes/objects

    /**
     * Output an identity attribute as an ISB xml attribute value.
     *
     * @param attribute the attribute
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeAttribute(final String attribute) throws IOException {
        String existingValue = writeValue(attribute, getExistingPerson());
        String updatedValue = writeValue(attribute, getUpdatedPerson());

        isbLogger.debug("Existing value: " + existingValue);
        isbLogger.debug("Updated value: " + updatedValue);

        boolean entityWritten = false;

        if (StringUtils.equals(getAction(), "create")) {
            writeAttribute(attribute, "new", existingValue, updatedValue);
            entityWritten = true;
        }
        if (StringUtils.equals(getAction(), "modify")) {
            writeAttribute(attribute, "updated", existingValue, updatedValue);
            entityWritten = true;
        }
        if (!entityWritten) {
            writeAttribute(attribute, "existing", existingValue, updatedValue);
        }
    }

    /**
     * Output an identity attribute as an ISB xml attribute value.
     *
     * @param attribute the attribute
     * @param state the state
     * @param existingValue the existing value
     * @param updatedValue the updated value
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeAttribute(final String attribute, final String state, final String existingValue,
            final String updatedValue) throws IOException {

        String existingVal = "";
        if (existingValue != null) {
            existingVal = existingValue;
        }

        XmlWriter valueWriter = new XmlWriter();

        boolean valueWritten = false;

        if (StringUtils.equals(state, "new")) {
            valueWriter.writeEntity("value").writeAttribute("state", "new");
            valueWriter.writeText(existingVal);
            valueWriter.endEntity();
            valueWritten = true;
        }
        if (StringUtils.equals(state, "deleted")) {
            valueWriter.writeEntity("value").writeAttribute("state", "deleted");
            valueWriter.writeText(existingVal);
            valueWriter.endEntity();
            valueWritten = true;
        }

        if (!valueWritten) {
            /**
             * The existing value 'should' be communicated. Unfortunately the
             * ISB currently only supports one attribute value, either existing
             * or the other action.
             */
            // xmlwriter.writeEntity("value").writeAttribute("state",
            // "existing");
            // xmlwriter.writeText(existingValue);
            // xmlwriter.endEntity();
            // Compare values - if different output,
            if (existingValue != null && updatedValue != null && !StringUtils.equals(existingValue, updatedValue)) {
                // The updated value is different to the existing value - output
                valueWriter.writeEntity("value").writeAttribute("state", state);
                valueWriter.writeText(updatedValue);
                valueWriter.endEntity();
                valueWritten = true;
            } else {
                // If multiple value support is added this block of code can be removed.
                if (StringUtils.isBlank(this.getAction()) || StringUtils.equals(this.getAction(), "delete")) {

                    String unmodifiedState = "existing";
                    if (StringUtils.equals(this.getAction(), "delete")) {
                        unmodifiedState = "deleted";
                    }

                    valueWriter.writeEntity("value").writeAttribute("state", unmodifiedState);
                    valueWriter.writeText(existingVal);
                    valueWriter.endEntity();
                    valueWritten = true;
                }
            }
        }

        if (valueWritten) {
            xmlwriter.writeEntity("attribute");
            xmlwriter.writeAttribute("name", attribute);

            xmlwriter.writeXml(valueWriter.getXml());

            xmlwriter.endEntity();
        }
        valueWriter = null;
    }

    /**
     * Output the roles of ISB entity as a series of attributes.
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void writeRoles() throws IOException {
        Map<String, String> existingMap = buildRoleMap(getExistingPerson());
        Map<String, String> updatedMap = buildRoleMap(getUpdatedPerson());

        boolean rolesProcessed = false;

        if (StringUtils.equals(getAction(), "create")) {
            // Iterate through existing roles on assumption they are all new
            for (String role : existingMap.values()) {
                writeAttribute("role", "new", role, null);
            }
            rolesProcessed = true;
        }
        if (StringUtils.equals(getAction(), "modify")) {
            // Iterate through all roles and see if they are
            // new, modified or deleted
            for (String roleId : existingMap.keySet()) {

                final String existingDN = existingMap.get(roleId);

                if (updatedMap.containsKey(roleId)) {
                    // Role has been modified
                    final String updatedDN = updatedMap.get(roleId);
                    if (!StringUtils.equals(existingDN, updatedDN)) {
                        // Role changed send a delete + create role action
                        writeAttribute("role", "deleted", existingDN, existingDN);
                        writeAttribute("role", "new", updatedDN, null);
                    }
                } else {
                    // Role has been deleted
                    writeAttribute("role", "deleted", existingDN, existingDN);
                }
            }
            for (String roleId : updatedMap.keySet()) {
                if (!existingMap.containsKey(roleId)) {
                    // Role has been created
                    final String roleDN = updatedMap.get(roleId);
                    writeAttribute("role", "new", roleDN, null);
                }
            }
            rolesProcessed = true;
        }
        if (!rolesProcessed) {
            // No action taken - snapshot requested
            for (String roleDN : existingMap.values()) {
                writeAttribute("role", "existing", roleDN, null);
            }
        }
    }

    /**
     * Constructs a Map< Integer, String > of the role DNs for a collection of
     * GroupBean objects.
     *
     * @param person the person
     *
     * @return the map< integer, string>
     */
    private Map<String, String> buildRoleMap(final PersonBean person) {

        Map<String, String> roleMap = new HashMap<String, String>();

        Map<String, String> groupRoleMap = buildGroupRoleMap(person);
        for (String guid : groupRoleMap.keySet()) {
            String role = groupRoleMap.get(guid);
            roleMap.put(guid, role);
        }

        Map<String, String> membershipRoleMap = buildMembershipRoleMap(person);
        for (String guid : membershipRoleMap.keySet()) {
            String role = membershipRoleMap.get(guid);
            roleMap.put(guid, role);
        }

        Map<String, String> buildSpecialtyRoleMap = buildSpecialtyRoleMap(person);
        for (String guid : buildSpecialtyRoleMap.keySet()) {
            String role = buildSpecialtyRoleMap.get(guid);
            roleMap.put(guid, role);
        }

        return roleMap;
    }

    /**
     * Output the related objects of an ISB entity to ISB xml.
     *
     * @param existingObjects the existing objects
     * @param updatedObjects the updated objects
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void processObjects(final ArrayList<Object> existingObjects, final ArrayList<Object> updatedObjects)
            throws IOException {
        boolean objectsProcessed = false;

        HashMap<Integer, Object> existingMap = new HashMap<Integer, Object>();
        HashMap<Integer, Object> updatedMap = new HashMap<Integer, Object>();

        for (Object object : existingObjects) {
            WhichDoctorBean wdObject = (WhichDoctorBean) object;
            existingMap.put(wdObject.getGUID(), object);
        }
        for (Object object : updatedObjects) {
            WhichDoctorBean wdObject = (WhichDoctorBean) object;
            updatedMap.put(wdObject.getGUID(), object);
        }

        if (StringUtils.equals(getAction(), "create")) {
            // Iterate through existing objects on assumption they are all new
            for (Object object : existingMap.values()) {
                writeObject(object, null, "new");
            }
            objectsProcessed = true;
        }
        if (StringUtils.equals(getAction(), "modify")) {
            // Iterate through all objects and see if they are new,
            // modified or deleted
            for (Object object : existingMap.values()) {
                WhichDoctorBean wdObject = (WhichDoctorBean) object;
                if (updatedMap.containsKey(wdObject.getGUID())) {
                    // Object has been modified
                    Object updatedObject = updatedMap.get(wdObject.getGUID());
                    // This has been disabled in favor of a simplier
                    // new/delete command set. Hopefully this changes in
                    // the future.
                    // writeObject(object, updatedObject, "updated");
                    if (compareObject(object, updatedObject)) {
                        // Only send through modified entries
                        writeObject(object, object, "deleted");
                        writeObject(updatedObject, null, "new");
                    }
                } else {
                    // Object has been deleted
                    writeObject(object, object, "deleted");
                }
            }
            for (Object object : updatedMap.values()) {
                WhichDoctorBean wdObject = (WhichDoctorBean) object;
                if (!existingMap.containsKey(wdObject.getGUID())) {
                    // Object has been created
                    writeObject(object, null, "new");
                }
            }
            objectsProcessed = true;
        }
        if (!objectsProcessed) {
            // No action taken - snapshot requested
            for (Object object : existingMap.values()) {
                writeObject(object, null, "existing");
            }
        }
    }

    /**
     * Checks if is trainee.
     *
     * @param person the person
     *
     * @return true, if is trainee
     */
    private boolean isTrainee(final PersonBean person) {
        boolean trainee = false;

        final String type = person.getMembershipField("Membership Type");

        if (StringUtils.equalsIgnoreCase(type, "Member: Basic Trainee")
                || StringUtils.equalsIgnoreCase(type, "Member: Advanced Trainee")) {
            trainee = true;
        }
        return trainee;
    }

    /**
     * Builds the group role map.
     *
     * @param person the person
     * @return the map
     */
    private Map<String, String> buildGroupRoleMap(final PersonBean person) {

        Map<String, String> roleMap = new HashMap<String, String>();

        Collection<GroupBean> groups = new ArrayList<GroupBean>();

        if (person != null && person.getGroups() != null) {
            groups = person.getGroups();
        }

        for (GroupBean role : groups) {
            if (StringUtils.isNotBlank(role.getGroupDN())) {
                boolean roleAdded = false;
                for (String key : role.getItems().keySet()) {
                    ItemBean item = role.getItems().get(key);
                    if (StringUtils.isNotBlank(item.getTitle())) {

                        String title = StringUtils.replace(item.getTitle().trim(), " ", "_");
                        title = StringUtils.replace(title, ",", "_");

                        StringBuffer roleDN = new StringBuffer();
                        roleDN.append("ou=");
                        roleDN.append(title);
                        roleDN.append(",");
                        roleDN.append(role.getGroupDN().trim());

                        roleMap.put(String.valueOf(item.getGUID()), roleDN.toString());
                        roleAdded = true;
                    }
                }
                if (!roleAdded) {
                    roleMap.put(String.valueOf(role.getGUID()), role.getGroupDN().trim());
                }
            }
        }
        return roleMap;
    }

    /**
     * Builds the membership role map.
     *
     * @param person the person
     * @return the map
     */
    private Map<String, String> buildMembershipRoleMap(final PersonBean person) {

        Map<String, String> roleMap = new HashMap<String, String>();

        Collection<MembershipBean> memberships = new ArrayList<MembershipBean>();

        if (person != null && person.getMembershipDetails() != null) {
            memberships = person.getMembershipDetails();
        }

        String prefix = "";

        for (MembershipBean role : memberships) {
            final String roleClass = role.getMembershipClass();
            final String roleType = role.getMembershipType();

            StringBuffer roleDN = new StringBuffer();

            if (StringUtils.equalsIgnoreCase(roleClass, "RACP") && StringUtils.isBlank(roleType)) {

                // The default membership type
                ObjectTypeBean div = role.getObjectTypeField("Division");
                ObjectTypeBean type = role.getObjectTypeField("Membership Type");

                if (type != null && StringUtils.isNotBlank(type.getLdapMapping(1))) {
                    roleDN.append(type.getLdapMapping(1));
                }

                if (div != null && StringUtils.isNotBlank(div.getLdapMapping(1))) {
                    prefix = div.getLdapMapping(1);
                }

                if (StringUtils.isNotBlank(roleDN.toString()) && StringUtils.isNotBlank(prefix)) {
                    roleDN.insert(0, ",");
                    roleDN.insert(0, prefix);
                }
            }
            if (StringUtils.equalsIgnoreCase(roleClass, "RACP")
                    && StringUtils.endsWithIgnoreCase(roleType, "Affiliation")) {
                ObjectTypeBean type = role.getObjectTypeField("Affiliation");

                if (type != null && StringUtils.isNotBlank(type.getLdapMapping(1))) {
                    roleDN.append(type.getLdapMapping(1));
                }
            }
            if (StringUtils.isNotBlank(roleDN.toString())) {
                roleMap.put(String.valueOf(role.getGUID()), roleDN.toString().trim());
            }
        }

        // Process the training status to see if it has a mapping
        if (person != null && StringUtils.isNotBlank(person.getTrainingStatusMapping())) {
            StringBuffer roleDN = new StringBuffer();
            roleDN.append(person.getTrainingStatusMapping());

            if (StringUtils.isNotBlank(prefix)) {
                roleDN.insert(0, ",");
                roleDN.insert(0, prefix);
            }
            roleMap.put(String.valueOf(person.getGUID()), roleDN.toString().trim());
        }

        return roleMap;
    }

    /**
     * Builds the specialty (training program/curriculum) role map.
     *
     * @param person the person
     * @return the map
     */
    private Map<String, String> buildSpecialtyRoleMap(final PersonBean person) {

        Map<String, String> roleMap = new HashMap<String, String>();

        Collection<SpecialtyBean> specialties = new ArrayList<SpecialtyBean>();

        if (person != null && person.getGroups() != null) {
            specialties = person.getSpecialtyList();
        }

        for (SpecialtyBean specialty : specialties) {
            if (StringUtils.equalsIgnoreCase(specialty.getStatus(), "In training")) {

                String prefix = "";
                if (specialty.getTrainingProgramYear() > 0) {
                    prefix = "ou=" + specialty.getTrainingProgramYear() + ",";
                }

                final int numberOfRoles = 3;
                for (int i = 1; i < numberOfRoles + 1; i++) {
                    if (StringUtils.isNotBlank(specialty.getIsbRole(i))) {
                        String role = prefix + specialty.getIsbRole(i).trim();
                        roleMap.put(String.valueOf(specialty.getGUID()) + "_" + i, role);
                    }
                }
            }
        }
        return roleMap;
    }

}