org.unitime.timetable.dataexchange.BaseCourseOfferingImport.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.timetable.dataexchange.BaseCourseOfferingImport.java

Source

/* 
 * UniTime 3.1 - 3.5 (University Timetabling Application)
 * Copyright (C) 2008 - 2013, UniTime LLC
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package org.unitime.timetable.dataexchange;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;

import org.dom4j.Element;
import org.hibernate.FlushMode;
import org.hibernate.engine.spi.SessionImplementor;
import org.unitime.commons.Debug;
import org.unitime.timetable.defaults.ApplicationProperty;
import org.unitime.timetable.model.ChangeLog;
import org.unitime.timetable.model.ClassEvent;
import org.unitime.timetable.model.ClassInstructor;
import org.unitime.timetable.model.Class_;
import org.unitime.timetable.model.CourseCreditUnitConfig;
import org.unitime.timetable.model.CourseOffering;
import org.unitime.timetable.model.DatePattern;
import org.unitime.timetable.model.Department;
import org.unitime.timetable.model.DepartmentalInstructor;
import org.unitime.timetable.model.DistributionObject;
import org.unitime.timetable.model.DistributionPref;
import org.unitime.timetable.model.DistributionType;
import org.unitime.timetable.model.FixedCreditUnitConfig;
import org.unitime.timetable.model.InstrOfferingConfig;
import org.unitime.timetable.model.InstructionalOffering;
import org.unitime.timetable.model.ItypeDesc;
import org.unitime.timetable.model.Meeting;
import org.unitime.timetable.model.NonUniversityLocation;
import org.unitime.timetable.model.OfferingConsentType;
import org.unitime.timetable.model.PreferenceGroup;
import org.unitime.timetable.model.PreferenceLevel;
import org.unitime.timetable.model.Room;
import org.unitime.timetable.model.RoomDept;
import org.unitime.timetable.model.SchedulingSubpart;
import org.unitime.timetable.model.Staff;
import org.unitime.timetable.model.SubjectArea;
import org.unitime.timetable.model.TimePattern;
import org.unitime.timetable.model.TimePatternDays;
import org.unitime.timetable.model.TimePatternTime;
import org.unitime.timetable.model.TimePref;
import org.unitime.timetable.model.VariableFixedCreditUnitConfig;
import org.unitime.timetable.model.VariableRangeCreditUnitConfig;
import org.unitime.timetable.model.dao.CourseOfferingDAO;
import org.unitime.timetable.test.MakeAssignmentsForClassEvents;
import org.unitime.timetable.util.CalendarUtils;
import org.unitime.timetable.util.Constants;
import org.unitime.timetable.util.InstrOfferingPermIdGenerator;

/**
 * @author Stephanie Schluttenhofer, Tomas Muller
 */
public abstract class BaseCourseOfferingImport extends EventRelatedImports {

    HashSet<Long> existingInstructionalOfferings = new HashSet<Long>();
    HashSet<Long> existingCourseOfferings = new HashSet<Long>();
    HashSet<Long> existingClasses = new HashSet<Long>();
    HashMap<String, SubjectArea> subjectAreas = new HashMap<String, SubjectArea>();
    HashMap<String, TimePattern> timePatterns = new HashMap<String, TimePattern>();
    HashMap<String, ItypeDesc> itypes = new HashMap<String, ItypeDesc>();
    HashMap<String, ItypeDesc> itypesBySisRef = new HashMap<String, ItypeDesc>();
    protected DistributionType meetsWithType = null;
    protected DistributionType canShareRoomType = null;
    boolean useMeetsWithElement = false;
    boolean useCanShareRoomElement = false;
    boolean incremental = false;
    PreferenceLevel requiredPrefLevel = null;
    MakeAssignmentsForClassEvents assignmentHelper = null;
    protected String rootElementName;
    int changeCount;
    protected boolean courseNumbersMustBeUnique;

    public BaseCourseOfferingImport() {
        super();
        changeCount = 0;

        courseNumbersMustBeUnique = ApplicationProperty.CourseOfferingNumberMustBeUnique.isTrue();

    }

    public void loadXml(Element rootElement) throws Exception {
        initializeTrimLeadingZeros();

        try {
            if (!rootElement.getName().equalsIgnoreCase(rootElementName)) {
                throw new Exception("Given XML file is not a Course Offerings load file.");
            }
            beginTransaction();

            incremental = "true".equalsIgnoreCase(rootElement.attributeValue("incremental", "false"));
            if (incremental)
                info("Incremental mode.");

            initializeLoad(rootElement, rootElementName);
            preLoadAction();
            loadOfferings(rootElement);

            if (!incremental) {
                deleteUnmatchedInstructionalOfferings();
                deleteUnmatchedCourseOfferings();
            }
            deleteUnmatchedClasses();
            commitTransaction();

        } catch (Exception e) {
            fatal("Exception: " + e.getMessage(), e);
            rollbackTransaction();
            throw e;
        } finally {
            postLoadAction();
        }
        addNote("Records Changed: " + changeCount);
        updateChangeList(true);
        reportMissingLocations();
        mailLoadResults();
    }

    // If a setup action needs to take place before the data is loaded override this method
    protected abstract void preLoadAction();

    // If a post load action needs to take place before the data is loaded override this method
    protected abstract void postLoadAction();

    protected void loadOfferings(Element rootElement) throws Exception {
        for (Iterator<?> it = rootElement.elementIterator(); it.hasNext();) {
            Element element = (Element) it.next();
            try {
                elementOffering(element);
                flush(true);
            } catch (Exception e) {
                addNote("Not Loading 'offering' Error:  " + e.getMessage());
                e.printStackTrace();
                addNote("\t " + element.asXML());
                updateChangeList(true);
            }
        }
    }

    protected void elementOffering(Element element) throws Exception {
        String offeringElementName = "offering";

        if (!element.getName().equalsIgnoreCase(offeringElementName)) {
            throw new Exception("Expecting to find an '" + offeringElementName + "' at this level, instead found '"
                    + element.getName() + "'.");
        }

        String externalId = getOptionalStringAttribute(element, "id");
        String action = getOptionalStringAttribute(element, "action");

        InstructionalOffering io = null;
        boolean existingIo = false;
        if (externalId != null) {
            io = findInstrOffrForExternalId(externalId, session.getUniqueId());
            if (io != null) {
                existingIo = true;
            }
        }
        if (io == null) {
            ArrayList<ImportCourseOffering> courses = getCourses(element);
            if (courses == null || courses.isEmpty()) {
                throw new Exception("Expected an 'offering' to have at least one course.");
            }
            if (courses.size() == 1) {
                ImportCourseOffering ico = (ImportCourseOffering) courses.get(0);
                CourseOffering co = ico.getCourseOffering();
                CourseOffering existingCourseOffering = findExistingCourseOffering(co, session.getUniqueId());
                if (existingCourseOffering == null) {
                    io = new InstructionalOffering();
                    io.setSession(session);
                    io.setExternalUniqueId(externalId);
                    io.setByReservationOnly(false);
                } else {
                    io = existingCourseOffering.getInstructionalOffering();
                    existingIo = true;
                }
            } else {
                HashSet<InstructionalOffering> possibleOfferings = new HashSet<InstructionalOffering>();
                for (Iterator<ImportCourseOffering> coIt = courses.iterator(); coIt.hasNext();) {
                    ImportCourseOffering ico = (ImportCourseOffering) coIt.next();
                    CourseOffering co = ico.getCourseOffering();
                    CourseOffering existingCourseOffering = findExistingCourseOffering(co, session.getUniqueId());
                    if (existingCourseOffering != null) {
                        possibleOfferings.add(existingCourseOffering.getInstructionalOffering());
                    }
                }
                if (possibleOfferings.isEmpty()) {
                    io = new InstructionalOffering();
                    io.setSession(session);
                    io.setExternalUniqueId(externalId);
                    io.setByReservationOnly(false);
                } else if (possibleOfferings.size() == 1) {
                    io = (InstructionalOffering) possibleOfferings.iterator().next();
                    existingIo = true;
                } else {
                    CourseOffering control = null;
                    for (Iterator<ImportCourseOffering> coIt = courses.iterator(); coIt.hasNext();) {
                        ImportCourseOffering ico = (ImportCourseOffering) coIt.next();
                        CourseOffering co = ico.getCourseOffering();
                        if (co.isIsControl().booleanValue()) {
                            control = co;
                            break;
                        }
                    }
                    if (control == null) {
                        throw new Exception("Expected an 'offering' to have a controlling course.");
                    }
                    InstructionalOffering offeringForControllingCourse = null;
                    for (Iterator<InstructionalOffering> ioIt = possibleOfferings.iterator(); ioIt.hasNext();) {
                        InstructionalOffering possibleIntructionalOffering = (InstructionalOffering) ioIt.next();
                        if (isSameCourseOffering(possibleIntructionalOffering.getControllingCourseOffering(),
                                control)) {
                            offeringForControllingCourse = possibleIntructionalOffering;
                            break;
                        }
                    }
                    if (offeringForControllingCourse != null) {
                        io = offeringForControllingCourse;
                        possibleOfferings.remove(io);
                        existingIo = true;
                        for (Iterator<InstructionalOffering> ioIt = possibleOfferings.iterator(); ioIt.hasNext();) {
                            InstructionalOffering oldIo = (InstructionalOffering) ioIt.next();
                            deleteInstructionalOffering(oldIo);
                        }
                    } else {
                        io = new InstructionalOffering();
                        io.setSession(session);
                        io.setExternalUniqueId(externalId);
                        io.setByReservationOnly(false);
                        for (Iterator<InstructionalOffering> ioIt = possibleOfferings.iterator(); ioIt.hasNext();) {
                            InstructionalOffering oldIo = (InstructionalOffering) ioIt.next();
                            deleteInstructionalOffering(oldIo);
                        }
                    }
                }
            }
        }
        if (externalId != null && io.getExternalUniqueId() != null
                && !io.getExternalUniqueId().equals(externalId)) {
            existingIo = false;
            deleteInstructionalOffering(io);
            io = new InstructionalOffering();
            io.setSession(session);
            io.setExternalUniqueId(externalId);
            io.setByReservationOnly(false);
        }

        if (existingIo && action != null && action.equalsIgnoreCase("delete")) {
            addNote("Deleted instructional offering: " + io.getCourseName());
            deleteInstructionalOffering(io);
            changeCount++;
        } else if (!existingIo && action != null && action.equalsIgnoreCase("delete")) {
            return;
        } else if (io != null) {
            if (existingIo) {
                if (!existingInstructionalOfferings.remove(io.getUniqueId())) {
                    throw new Exception("could not remove io uniqueid from existing");
                }
                action = "update";
                addNote("Changes for instructional offering: " + io.getCourseName());
            } else {
                action = "insert";
            }
            if (doAddUpdate(element, io, action) || action.equalsIgnoreCase("insert")) {
                changeCount++;
                if (action.equals("insert")) {
                    clearNotes();
                    addNote("Added instructional offering: "
                            + io.getControllingCourseOffering().getSubjectArea().getSubjectAreaAbbreviation() + " "
                            + io.getControllingCourseOffering().getCourseNbr());
                }
                updateChangeList(true);
            } else {
                updateChangeList(false);
            }
        } else {
            return;
        }

    }

    protected void deleteUnmatchedInstructionalOfferings() {
        if (existingInstructionalOfferings.size() > 0) {
            HashSet<Long> deleteOfferings = new HashSet<Long>();
            deleteOfferings.addAll(existingInstructionalOfferings);
            addNote("Deleted Instructional Offerings that were not in the input file:");
            for (Iterator<Long> ioIt = deleteOfferings.iterator(); ioIt.hasNext();) {
                Long uniqueId = (Long) ioIt.next();
                InstructionalOffering unusedIo = findInstrOffrForUniqueId(uniqueId);
                if (unusedIo != null) {
                    addNote("\tdeleted: " + unusedIo.getCourseName());
                    deleteInstructionalOffering(unusedIo);
                    changeCount++;
                }
            }
            flushIfNeeded(true);
            updateChangeList(true);
        }

    }

    protected void deleteUnmatchedCourseOfferings() {
        if (existingCourseOfferings.size() > 0) {
            HashSet<Long> deleteOfferings = new HashSet<Long>();
            deleteOfferings.addAll(existingCourseOfferings);
            addNote("Deleted Course Offerings that were not in the input file:");
            for (Iterator<Long> coIt = deleteOfferings.iterator(); coIt.hasNext();) {
                Long uniqueId = (Long) coIt.next();
                CourseOffering unusedCo = findCourseOffrForUniqueId(uniqueId);
                if (unusedCo != null) {
                    addNote("\tDeleted: " + unusedCo.getCourseName());
                    deleteCourseOffering(unusedCo);
                    changeCount++;
                }
            }
            flushIfNeeded(true);
            updateChangeList(true);
        }
    }

    protected void deleteUnmatchedClasses() {
        if (existingClasses.size() > 0) {
            HashSet<Long> deleteClasses = new HashSet<Long>();
            deleteClasses.addAll(existingClasses);
            addNote("Deleted Classes that were not in the input file:");
            for (Iterator<Long> cIt = deleteClasses.iterator(); cIt.hasNext();) {
                Long uniqueId = (Long) cIt.next();
                Class_ unusedC = findClassForUniqueId(uniqueId);
                if (unusedC != null) {
                    if (incremental) {
                        try {
                            if (existingInstructionalOfferings
                                    .contains(unusedC.getSchedulingSubpart().getInstrOfferingConfig()
                                            .getInstructionalOffering().getUniqueId())
                                    || existingCourseOfferings.contains(unusedC.getSchedulingSubpart()
                                            .getInstrOfferingConfig().getInstructionalOffering()
                                            .getControllingCourseOffering().getUniqueId()))
                                continue;
                        } catch (NullPointerException e) {
                        }
                    }
                    addNote("\tDeleted: " + unusedC.getClassLabel());
                    deleteClass(unusedC);
                    changeCount++;
                }
            }
            flushIfNeeded(true);
            updateChangeList(true);
        }
    }

    protected void initializeLoad(Element rootElement, String rootElementName) throws Exception {
        initializeDateTimeFormats(rootElement);
        initializeSessionData(rootElement, rootElementName);
        initializeMeetsWith(rootElement);
        initializeCanShareRoom(rootElement);
        initializeAssignmentHelper();
        loadSetupData();
        logXmlFileCreateInformation(rootElement);
    }

    protected void logXmlFileCreateInformation(Element rootElement) {
        String created = getOptionalStringAttribute(rootElement, "created");
        if (created != null) {
            addNote("Loading offerings XML file created on: " + created);
            ChangeLog.addChange(getHibSession(), getManager(), session, session, created,
                    ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.UPDATE, null, null);
            updateChangeList(true);
        }
    }

    protected void initializeAssignmentHelper() {
        assignmentHelper = new MakeAssignmentsForClassEvents(session, getHibSession());
    }

    protected void initializeMeetsWith(Element rootElement) {
        Boolean useMeetsWith = getOptionalBooleanAttribute(rootElement, "useMeetsWith");
        if (useMeetsWith != null && useMeetsWith.booleanValue()) {
            useMeetsWithElement = true;
        }
    }

    protected void initializeCanShareRoom(Element rootElement) {
        Boolean useCanShareRoom = getOptionalBooleanAttribute(rootElement, "useCanShareRoom");
        if (useCanShareRoom != null && useCanShareRoom.booleanValue()) {
            useCanShareRoomElement = true;
        }
    }

    protected void initializeSessionData(Element rootElement, String rootElementName) throws Exception {
        String campus = getRequiredStringAttribute(rootElement, "campus", rootElementName);
        String year = getRequiredStringAttribute(rootElement, "year", rootElementName);
        String term = getRequiredStringAttribute(rootElement, "term", rootElementName);
        session = findSession(campus, year, term);
        if (session == null) {
            throw new Exception("No session found for the given campus, year, and term.");
        }
    }

    protected void initializeDateTimeFormats(Element rootElement) {
        dateFormat = getOptionalStringAttribute(rootElement, "dateFormat");
        timeFormat = getOptionalStringAttribute(rootElement, "timeFormat");
        if (timeFormat == null) {
            timeFormat = "HHmm";
        }
    }

    protected void initializeTrimLeadingZeros() {
        trimLeadingZerosFromExternalId = ApplicationProperty.DataExchangeTrimLeadingZerosFromExternalIds.isTrue();
    }

    protected void loadSetupData() throws Exception {
        loadItypes();
        loadSubjectAreas(session.getUniqueId());
        loadTimePatterns(session.getUniqueId());
        loadExistingInstructionalOfferings(session.getUniqueId());
        loadExistingCourseOfferings(session.getUniqueId());
        loadExistingClasses(session.getUniqueId());
        loadRequiredPrefLevel();
        loadMeetsWithDistributionType();
        loadCanShareRoomDistributionType();

    }

    private void loadTimePatterns(Long sessionId) {
        List<?> patterns = new ArrayList<Object>();
        patterns = this.getHibSession().createQuery(
                "select distinct tp from TimePattern as tp where tp.session.uniqueId=:sessionId and ( tp.type = :standard or tp.type = :evening )")
                .setLong("sessionId", sessionId.longValue()).setInteger("standard", TimePattern.sTypeStandard)
                .setInteger("evening", TimePattern.sTypeEvening).setCacheable(true).list();
        for (Iterator<?> tpIt = patterns.iterator(); tpIt.hasNext();) {
            TimePattern tp = (TimePattern) tpIt.next();
            for (Iterator<?> dIt = tp.getDays().iterator(); dIt.hasNext();) {
                TimePatternDays tpd = (TimePatternDays) dIt.next();
                for (Iterator<?> timesIt = tp.getTimes().iterator(); timesIt.hasNext();) {
                    TimePatternTime tpt = (TimePatternTime) timesIt.next();
                    timePatterns.put((tpd.getDayCode().toString() + "x" + tp.getMinPerMtg().toString() + "x"
                            + tpt.getStartSlot()), tp);
                }
            }
        }
    }

    private boolean isSameCourseOffering(CourseOffering originalCourseOffering, CourseOffering newCourseOffering) {
        boolean isSame = false;
        if (originalCourseOffering.getExternalUniqueId() != null
                && originalCourseOffering.getExternalUniqueId().equals(newCourseOffering.getExternalUniqueId())) {
            isSame = true;
        } else {
            if (courseNumbersMustBeUnique) {
                if (originalCourseOffering.getSubjectArea().getUniqueId()
                        .equals(newCourseOffering.getSubjectArea().getUniqueId())
                        && originalCourseOffering.getCourseNbr().equals(newCourseOffering.getCourseNbr())) {
                    isSame = true;
                }
            } else {
                if (originalCourseOffering.getSubjectArea().getUniqueId()
                        .equals(newCourseOffering.getSubjectArea().getUniqueId())
                        && originalCourseOffering.getCourseNbr().equals(newCourseOffering.getCourseNbr())
                        && originalCourseOffering.getTitle().equals(newCourseOffering.getTitle())) {
                    isSame = true;
                }
            }
        }
        return (isSame);
    }

    private boolean isSameCreditConfig(CourseCreditUnitConfig originalCourseCreditUnitConfig,
            CourseCreditUnitConfig courseCreditUnitConfig) {
        boolean different = false;
        if (originalCourseCreditUnitConfig.getCreditFormat() == null
                || courseCreditUnitConfig.getCreditFormat() == null || (originalCourseCreditUnitConfig
                        .getCreditFormat().equals(courseCreditUnitConfig.getCreditFormat()))) {
            if (!originalCourseCreditUnitConfig.getCreditType().getUniqueId()
                    .equals(courseCreditUnitConfig.getCreditType().getUniqueId())) {
                different = true;
            } else if (!originalCourseCreditUnitConfig.getCreditUnitType().getUniqueId()
                    .equals(courseCreditUnitConfig.getCreditUnitType().getUniqueId())) {
                different = true;
            } else {
                if (originalCourseCreditUnitConfig instanceof FixedCreditUnitConfig) {
                    FixedCreditUnitConfig fcuc = (FixedCreditUnitConfig) originalCourseCreditUnitConfig;
                    FixedCreditUnitConfig newFcuc = (FixedCreditUnitConfig) courseCreditUnitConfig;
                    if (!fcuc.getFixedUnits().equals(newFcuc.getFixedUnits())) {
                        different = true;
                    }
                } else if (originalCourseCreditUnitConfig instanceof VariableRangeCreditUnitConfig) {
                    VariableRangeCreditUnitConfig vrcuc = (VariableRangeCreditUnitConfig) originalCourseCreditUnitConfig;
                    VariableRangeCreditUnitConfig newVrcuc = (VariableRangeCreditUnitConfig) courseCreditUnitConfig;
                    if (!vrcuc.getMinUnits().equals(newVrcuc.getMinUnits())) {
                        different = true;
                    } else if (!vrcuc.getMaxUnits().equals(newVrcuc.getMaxUnits())) {
                        different = true;
                    } else if (!vrcuc.isFractionalIncrementsAllowed()
                            .equals(newVrcuc.isFractionalIncrementsAllowed())) {
                        different = true;
                    }
                } else if (originalCourseCreditUnitConfig instanceof VariableFixedCreditUnitConfig) {
                    VariableFixedCreditUnitConfig vfcuc = (VariableFixedCreditUnitConfig) originalCourseCreditUnitConfig;
                    VariableFixedCreditUnitConfig newVfcuc = (VariableFixedCreditUnitConfig) courseCreditUnitConfig;
                    if (!vfcuc.getMinUnits().equals(newVfcuc.getMinUnits())) {
                        different = true;
                    } else if (!vfcuc.getMaxUnits().equals(newVfcuc.getMaxUnits())) {
                        different = true;
                    }
                }
            }

        } else {
            different = true;
        }
        if (different) {
            return (false);
        } else {
            return (true);
        }

    }

    private boolean isSameMeeting(Meeting originalMeeting, Meeting newMeeting) {
        boolean isSame = false;
        if (getDateString(originalMeeting.getMeetingDate()).equals(getDateString(newMeeting.getMeetingDate()))
                && ((originalMeeting.getLocationPermanentId() != null && newMeeting.getLocationPermanentId() != null
                        && originalMeeting.getLocationPermanentId().longValue() == newMeeting
                                .getLocationPermanentId().longValue())
                        || (originalMeeting.getLocationPermanentId() == null
                                && newMeeting.getLocationPermanentId() == null))
                && originalMeeting.getStartOffset().intValue() == newMeeting.getStartOffset().intValue()
                && originalMeeting.getStartPeriod().intValue() == newMeeting.getStartPeriod().intValue()
                && originalMeeting.getStopOffset().intValue() == newMeeting.getStopOffset().intValue()
                && originalMeeting.getStopPeriod().intValue() == newMeeting.getStopPeriod().intValue()) {
            isSame = true;
        }
        return (isSame);
    }

    private boolean doAddUpdate(Element element, InstructionalOffering io, String action) throws Exception {
        boolean changed = false;

        if (io.getInstrOfferingPermId() == null) {
            io.generateInstrOfferingPermId();
        }

        Boolean offered = getRequiredBooleanAttribute(element, "offered", "offered");
        if (io.isNotOffered() == null || io.isNotOffered().equals(offered)) {
            io.setNotOffered(new Boolean(!offered.booleanValue()));
            addNote("\toffered status changed");
            changed = true;
        }

        if (elementCourse(element, io, action)) {
            addNote("\tcourses changed");
            changed = true;
        }

        if (changed) {
            this.getHibSession().saveOrUpdate(io);
        }

        if (elementInstrOffrConfig(element, io)) {
            changed = true;
        }

        if (changed) {
            this.getHibSession().saveOrUpdate(io);
            ChangeLog.addChange(getHibSession(), getManager(), session, io, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                    (action.equalsIgnoreCase("insert") ? ChangeLog.Operation.CREATE : ChangeLog.Operation.UPDATE),
                    io.getControllingCourseOffering().getSubjectArea(), io.getDepartment());
        }

        return (changed);
    }

    private DepartmentalInstructor findDepartmentalInstructorWithExternalUniqueId(String externalId,
            Department department) {
        return (DepartmentalInstructor) this.getHibSession().createQuery(
                "select distinct di from DepartmentalInstructor di where di.externalUniqueId=:externalId and di.department.uniqueId=:departmentId")
                .setString("externalId", externalId).setLong("departmentId", department.getUniqueId())
                .setCacheable(true).uniqueResult();
    }

    private String createPatternString(Vector<Calendar> startDates, Vector<Calendar> endDates) {
        Iterator<Calendar> startDateIt = startDates.iterator();
        Iterator<Calendar> endDateIt = endDates.iterator();

        StringBuffer patternString = new StringBuffer();
        Calendar lastDate = null;
        while (startDateIt.hasNext() && endDateIt.hasNext()) {
            Calendar startDate = Calendar.getInstance();
            startDate.setTime(((Calendar) startDateIt.next()).getTime());

            Calendar endDate = Calendar.getInstance();
            endDate.setTime(((Calendar) endDateIt.next()).getTime());

            if (lastDate != null) {
                lastDate.add(Calendar.DAY_OF_MONTH, 1);
                while (getCalendarDateString(lastDate).compareTo(getCalendarDateString(startDate)) < 0) {
                    patternString.append("0");
                    lastDate.add(Calendar.DAY_OF_MONTH, 1);
                }
            }
            lastDate = endDate;

            while (getCalendarDateString(startDate).compareTo(getCalendarDateString(endDate)) <= 0) {
                patternString.append("1");
                startDate.add(Calendar.DAY_OF_MONTH, 1);
            }
        }
        return (patternString.toString());
    }

    private DatePattern findDatePattern(Vector<Calendar> startDates, Vector<Calendar> endDates, Class_ c) {
        //Calculate offset from start of session
        Calendar firstDate = Calendar.getInstance();
        firstDate.setTime(((Calendar) startDates.firstElement()).getTime());
        Calendar sessionStartDate = Calendar.getInstance();
        sessionStartDate.setTime(session.getSessionBeginDateTime());
        int offset = 0;
        if (getCalendarDateString(firstDate).compareTo(getCalendarDateString(sessionStartDate)) < 0) {
            while (getCalendarDateString(firstDate).compareTo(getCalendarDateString(sessionStartDate)) < 0) {
                offset++;
                firstDate.add(Calendar.DAY_OF_MONTH, 1);
            }
        } else if (getCalendarDateString(firstDate).compareTo(getCalendarDateString(sessionStartDate)) > 0) {
            while (getCalendarDateString(firstDate).compareTo(getCalendarDateString(sessionStartDate)) > 0) {
                offset--;
                firstDate.add(Calendar.DAY_OF_MONTH, -1);
            }
        }
        String pattern = createPatternString(startDates, endDates);
        DatePattern dp = null;

        List patterns = this.getHibSession().createQuery(
                "from DatePattern as d where d.session.uniqueId = :sessionId and d.pattern = :pattern and d.offset = :offset and d.type = (select min(dd.type) from DatePattern as dd where dd.session.uniqueId = :sessionId and dd.pattern = :pattern and dd.offset = :offset)")
                .setLong("sessionId", session.getUniqueId().longValue()).setString("pattern", pattern)
                .setInteger("offset", offset).setCacheable(true).list();

        if (!patterns.isEmpty()) {
            dp = (DatePattern) patterns.get(0);
        } else {
            dp = new DatePattern();
            dp.setName("import - " + c.getClassLabel());
            dp.setPattern(pattern);
            dp.setOffset(new Integer(offset));
            dp.setSession(session);
            dp.setType(new Integer(3));
            dp.setVisible(new Boolean(false));
            this.getHibSession().save(dp);
            this.getHibSession().flush();
            this.getHibSession().refresh(dp);
            ChangeLog.addChange(getHibSession(), getManager(), session, dp, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                    ChangeLog.Operation.CREATE,
                    c.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                    c.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
        }
        if (dp.isDefault()) {
            return (null);
        } else {
            return (dp);
        }

    }

    protected ArrayList<ImportCourseOffering> getCourses(Element element) throws Exception {
        ArrayList<ImportCourseOffering> courses = new ArrayList<ImportCourseOffering>();
        String elementName = "course";
        if (element.element(elementName) != null) {
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element courseElement = (Element) it.next();

                String externalUid = getOptionalStringAttribute(courseElement, "id");
                Boolean controlling = getRequiredBooleanAttribute(courseElement, "controlling", elementName);
                String courseNbr = getRequiredStringAttribute(courseElement, "courseNbr", elementName);
                String scheduleBookNote = getOptionalStringAttribute(courseElement, "scheduleBookNote");
                String subjAbbv = getRequiredStringAttribute(courseElement, "subject", elementName);
                SubjectArea subjectArea = subjectAreas.get(subjAbbv);
                String title = getOptionalStringAttribute(courseElement, "title");
                CourseOffering newCourseOffering = new CourseOffering();
                newCourseOffering.setSubjectArea(subjectArea);
                if (courseNbr != null && courseNbr.trim().length() > 0) {
                    newCourseOffering.setCourseNbr(courseNbr.trim());
                }
                if (externalUid != null && externalUid.trim().length() > 0) {
                    newCourseOffering.setExternalUniqueId(externalUid.trim());
                }
                newCourseOffering.setIsControl(controlling);
                if (scheduleBookNote != null && scheduleBookNote.trim().length() > 0) {
                    newCourseOffering.setScheduleBookNote(scheduleBookNote.trim());
                }
                if (title != null && title.trim().length() > 0) {
                    newCourseOffering.setTitle(title.trim());
                }

                Element consentElement = courseElement.element("consent");
                if (consentElement == null)
                    consentElement = element.element("consent");
                if (consentElement != null) { // no consent on course -- try to take consent from the parent (offering)
                    String consentType = getRequiredStringAttribute(consentElement, "type", "consent");
                    newCourseOffering
                            .setConsentType(OfferingConsentType.getOfferingConsentTypeForReference(consentType));
                }

                elementCourseCredit(courseElement, newCourseOffering);

                ImportCourseOffering importcourseOffering = new ImportCourseOffering(newCourseOffering,
                        courseElement);
                courses.add(importcourseOffering);
            }
        } else {
            throw new Exception("'course' element is required.");
        }

        return (courses);
    }

    private String getDateString(Date date) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
        return (df.format(date));
    }

    private String getCalendarDateString(Calendar calendar) {
        return (getDateString(calendar.getTime()));
    }

    private Calendar getCalendarForDate(String date) {
        if (date.length() < 3 || date.indexOf("/") < 0) {
            return (null);
        }
        Calendar cal = Calendar.getInstance();
        int index1 = date.indexOf("/");
        int index2 = date.lastIndexOf("/");
        if (index2 == index1) {
            index2 = date.length();
        }
        int month = Integer.parseInt(date.substring(0, index1));
        int day = Integer.parseInt(date.substring(index1 + 1, index2));

        int year;
        if (index2 == date.length()) {
            year = session.getSessionStartYear();
        } else {
            year = Integer.parseInt(date.substring(index2 + 1, date.length()));
        }

        cal.set(year, (month - 1), day, 0, 0, 0);

        return (cal);
    }

    private HashMap<String, Vector<Calendar>> elementDates(Element element) throws Exception {
        Vector<Calendar> startDates = new Vector<Calendar>();
        Vector<Calendar> endDates = new Vector<Calendar>();
        String elementName = "date";
        if (element.element(elementName) != null) {
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element dateElement = (Element) it.next();
                Calendar startDate = null;
                Calendar endDate = null;
                if (dateFormat == null) {
                    startDate = getCalendarForDate(
                            getRequiredStringAttribute(dateElement, "startDate", elementName));
                    endDate = getCalendarForDate(getRequiredStringAttribute(dateElement, "endDate", elementName));
                    ;
                } else {
                    startDate = Calendar.getInstance();
                    startDate.setTime(CalendarUtils.getDate(
                            getRequiredStringAttribute(dateElement, "startDate", elementName), dateFormat));
                    endDate = Calendar.getInstance();
                    endDate.setTime(CalendarUtils
                            .getDate(getRequiredStringAttribute(dateElement, "endDate", elementName), dateFormat));
                }
                if (startDate == null) {
                    throw new Exception("For element 'date' a 'startDate' is required, unable to parse given date");
                }
                if (endDate == null) {
                    throw new Exception("For element 'date' a 'endDate' is required, unable to parse given date");
                }
                if (endDate.before(startDate)) {
                    endDate.add(Calendar.YEAR, 1);
                }
                startDates.add(startDate);
                endDates.add(endDate);
            }
        }
        if (startDates.size() > 0) {
            HashMap<String, Vector<Calendar>> dates = new HashMap<String, Vector<Calendar>>();
            dates.put("startDates", startDates);
            dates.put("endDates", endDates);
            return (dates);
        } else {
            return (null);
        }
    }

    private TimeObject elementTime(Element element) throws Exception {
        TimeObject meetingTime = null;
        String elementName = "time";
        if (element.element(elementName) != null) {
            Element timeElement = element.element(elementName);

            String startTime = getRequiredStringAttribute(timeElement, "startTime", elementName);
            String endTime = getRequiredStringAttribute(timeElement, "endTime", elementName);
            String days = getRequiredStringAttribute(timeElement, "days", elementName);
            meetingTime = new TimeObject(startTime, endTime, days);
            if (meetingTime.getDays() == null || meetingTime.getDays().isEmpty()) {
                meetingTime = null;
            }
        }
        return (meetingTime);
    }

    private Vector<Room> elementRoom(Element element, Class_ c) throws Exception {
        Vector<Room> rooms = new Vector<Room>();
        String elementName = "room";
        if (element.element(elementName) != null) {
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element roomElement = (Element) it.next();

                String building = getRequiredStringAttribute(roomElement, "building", elementName);
                String roomNbr = getRequiredStringAttribute(roomElement, "roomNbr", elementName);
                String id = getOptionalStringAttribute(roomElement, "id");
                Room room = findRoom(id, building, roomNbr);

                if (room != null) {
                    rooms.add(room);
                } else {
                    addMissingLocation(building + " " + roomNbr + " - " + c.getSchedulingSubpart()
                            .getControllingCourseOffering().getSubjectArea().getSubjectAreaAbbreviation());
                }
            }
        }
        if (rooms.isEmpty()) {
            return (null);
        } else {
            return (rooms);
        }
    }

    private Vector<NonUniversityLocation> elementLocation(Element element, Class_ c) throws Exception {
        Vector<NonUniversityLocation> locations = new Vector<NonUniversityLocation>();
        String elementName = "location";
        if (element.element(elementName) != null) {
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element roomElement = (Element) it.next();

                String name = getRequiredStringAttribute(roomElement, "name", elementName);
                String id = getOptionalStringAttribute(roomElement, "id");

                NonUniversityLocation location = findNonUniversityLocation(id, name, c);
                if (location != null) {
                    locations.add(location);
                } else {
                    addMissingLocation(name + " - " + c.getSchedulingSubpart().getControllingCourseOffering()
                            .getSubjectArea().getSubjectAreaAbbreviation());
                    addMissingLocation("\tCould not find location '" + name + "' not adding it to class '"
                            + c.getClassLabel() + "'");
                }
            }
        }
        if (locations.isEmpty()) {
            return (null);
        } else {
            return (locations);
        }
    }

    private boolean elementMeetings(Element element, Class_ c) throws Exception {
        String elementName = "meeting";
        boolean changed = false;
        Vector<Meeting> meetings = new Vector<Meeting>();
        if (element.element(elementName) != null) {
            Calendar sessionStartDate = Calendar.getInstance();
            sessionStartDate.setTime(session.getSessionBeginDateTime());

            Calendar sessionClassesEndDate = Calendar.getInstance();
            sessionClassesEndDate.setTime(session.getExamBeginDate());
            sessionClassesEndDate.add(Calendar.DAY_OF_MONTH, -1);

            Calendar sessionEndDate = Calendar.getInstance();
            sessionEndDate.setTime(session.getSessionEndDateTime());

            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element meetingElement = (Element) it.next();
                String startDateStr = getOptionalStringAttribute(meetingElement, "startDate");
                String endDateStr = getOptionalStringAttribute(meetingElement, "endDate");
                String startTime = getRequiredStringAttribute(meetingElement, "startTime", elementName);
                String endTime = getRequiredStringAttribute(meetingElement, "endTime", elementName);
                String days = getRequiredStringAttribute(meetingElement, "days", elementName);
                String building = getOptionalStringAttribute(meetingElement, "building");
                String roomNbr = getOptionalStringAttribute(meetingElement, "room");
                String location = getOptionalStringAttribute(meetingElement, "location");
                Calendar startDate = null;
                Calendar endDate = null;
                if (startDateStr == null && endDateStr == null) {
                    startDate = sessionStartDate;
                    endDate = sessionClassesEndDate;
                } else if (dateFormat != null) {
                    startDate = Calendar.getInstance();
                    startDate.setTime(CalendarUtils.getDate(startDateStr, dateFormat));
                    endDate = Calendar.getInstance();
                    endDate.setTime(CalendarUtils.getDate(endDateStr, dateFormat));
                } else {
                    startDate = getCalendarForDate(startDateStr);
                    endDate = getCalendarForDate(endDateStr);
                }
                if (endDate.before(startDate)) {
                    endDate.add(Calendar.YEAR, 1);
                }

                if (endDate.equals(sessionEndDate)
                        || (endDate.before(sessionEndDate) && endDate.after(sessionClassesEndDate))) {
                    if (startDate.before(sessionClassesEndDate)) {
                        endDate = sessionClassesEndDate;
                    }
                }

                TimeObject timeObject = new TimeObject(startTime, endTime, days);

                Vector<Room> rooms = new Vector<Room>();
                Vector<NonUniversityLocation> nonUniversityLocations = new Vector<NonUniversityLocation>();

                if (building != null && roomNbr != null) {
                    Room r = findRoom(null, building, roomNbr);
                    if (r != null) {
                        rooms.add(r);
                    } else {
                        addMissingLocation(building + " " + roomNbr + " - " + c.getSchedulingSubpart()
                                .getControllingCourseOffering().getSubjectArea().getSubjectAreaAbbreviation());
                    }
                } else if (location != null) {
                    NonUniversityLocation nul = findNonUniversityLocation(null, location, c);
                    if (nul != null) {
                        nonUniversityLocations.add(nul);
                    } else {
                        addMissingLocation(location + " - " + c.getSchedulingSubpart()
                                .getControllingCourseOffering().getSubjectArea().getSubjectAreaAbbreviation());
                    }
                }

                Vector<Calendar> startDates = new Vector<Calendar>();
                startDates.add(startDate);

                Vector<Calendar> endDates = new Vector<Calendar>();
                endDates.add(endDate);

                Vector<Meeting> m = null;
                if (startDate.equals(sessionStartDate)
                        && (endDate.equals(sessionClassesEndDate) || endDate.equals(sessionEndDate))) {
                    m = getMeetings(session.getDefaultDatePattern(), timeObject, rooms, nonUniversityLocations);
                } else {
                    m = getMeetings(startDate.getTime(), endDate.getTime(),
                            createPatternString(startDates, endDates), timeObject, rooms, nonUniversityLocations);
                }
                if (m != null && !m.isEmpty()) {
                    meetings.addAll(m);
                }
            }
            changed = addUpdateClassEvent(c, meetings);
            if (changed)
                c.setDatePattern(assignmentHelper.getDatePattern(c.getEvent()));
        }

        return (changed);
    }

    private boolean elementInstructor(Element element, Class_ c) throws Exception {
        boolean changed = false;
        String elementName = "instructor";
        HashMap<String, ClassInstructor> existingInstructors = new HashMap<String, ClassInstructor>();
        if (c.getClassInstructors() != null) {
            for (Iterator<?> ciIt = c.getClassInstructors().iterator(); ciIt.hasNext();) {
                ClassInstructor ci = (ClassInstructor) ciIt.next();
                existingInstructors.put(ci.getInstructor().getExternalUniqueId(), ci);
            }
        }
        if (element.element(elementName) != null) {
            HashSet<String> ids = new HashSet<String>();
            HashMap<String, String> firstNames = new HashMap<String, String>();
            HashMap<String, String> middleNames = new HashMap<String, String>();
            HashMap<String, String> lastNames = new HashMap<String, String>();
            HashMap<String, String> acadTitles = new HashMap<String, String>();
            HashMap<String, Integer> shares = new HashMap<String, Integer>();
            HashMap<String, Boolean> leads = new HashMap<String, Boolean>();
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element instructorElement = (Element) it.next();
                String id = getRequiredStringAttribute(instructorElement, "id", elementName);
                if (trimLeadingZerosFromExternalId) {
                    try {
                        Integer num = new Integer(id);
                        id = num.toString();
                    } catch (Exception e) {
                        // do nothing
                    }
                }
                ids.add(id);
                firstNames.put(id, getOptionalStringAttribute(instructorElement, "fname"));
                middleNames.put(id, getOptionalStringAttribute(instructorElement, "mname"));
                lastNames.put(id, getOptionalStringAttribute(instructorElement, "lname"));
                acadTitles.put(id, getOptionalStringAttribute(instructorElement, "title"));
                Integer share = getOptionalIntegerAttribute(instructorElement, "share");
                ;
                if (share == null) {
                    share = new Integer(100);
                }
                shares.put(id, share);
                Boolean lead = getOptionalBooleanAttribute(instructorElement, "lead");
                if (lead == null) {
                    lead = new Boolean(true);
                }
                leads.put(id, lead);
            }
            for (Iterator<String> it = ids.iterator(); it.hasNext();) {
                boolean addNew = false;
                String id = (String) it.next();
                ClassInstructor ci = existingInstructors.get(id);
                if (ci == null) {
                    DepartmentalInstructor di = findDepartmentalInstructorWithExternalUniqueId(id,
                            c.getSchedulingSubpart().getControllingDept());
                    if (di == null) {
                        di = new DepartmentalInstructor();
                        di.setDepartment(c.getSchedulingSubpart().getControllingDept());
                        di.setExternalUniqueId(id);
                        if (lastNames.get(id) == null) {
                            Staff staffData = findStaffMember(id);
                            if (staffData != null) {
                                firstNames.put(id, staffData.getFirstName());
                                middleNames.put(id, staffData.getMiddleName());
                                lastNames.put(id, staffData.getLastName());
                                acadTitles.put(id, staffData.getAcademicTitle());
                            }
                        }
                        di.setFirstName(firstNames.get(id));
                        di.setMiddleName(middleNames.get(id));
                        di.setAcademicTitle(acadTitles.get(id));
                        String lastName = lastNames.get(id);
                        di.setLastName((lastName != null ? lastName : "Unknown Name"));
                        di.setIgnoreToFar(new Boolean(false));
                        getHibSession().save(di);
                        getHibSession().flush();
                        getHibSession().refresh(di);
                        ChangeLog.addChange(getHibSession(), getManager(), session, di,
                                ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.CREATE,
                                c.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                                c.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
                    }
                    ci = new ClassInstructor();
                    ci.setClassInstructing(c);
                    c.addToclassInstructors(ci);
                    ci.setInstructor(di);
                    di.addToclasses(ci);
                    changed = true;
                    addNew = true;

                } else {
                    existingInstructors.remove(id);
                }
                Integer share = shares.get(id);
                if (ci.getPercentShare() == null || !ci.getPercentShare().equals(share)) {
                    ci.setPercentShare(share);
                    changed = true;
                }

                Boolean lead = leads.get(id);
                if (ci.isLead() == null || !ci.isLead().equals(lead)) {
                    ci.setLead(lead);
                    changed = true;
                }

                if (changed) {
                    getHibSession().saveOrUpdate(c);
                    getHibSession().flush();
                    getHibSession().refresh(c);
                    ChangeLog.addChange(getHibSession(), getManager(), session, ci,
                            ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                            (addNew ? ChangeLog.Operation.CREATE : ChangeLog.Operation.UPDATE),
                            c.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                            c.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
                }
            }
        }
        if (existingInstructors.size() > 0) {
            for (Iterator<ClassInstructor> ciIt = existingInstructors.values().iterator(); ciIt.hasNext();) {
                ClassInstructor ci = (ClassInstructor) ciIt.next();
                deleteClassInstructor(ci);
                changed = true;
            }
        }
        return (changed);
    }

    private Staff findStaffMember(String id) {
        return (Staff) this.getHibSession()
                .createQuery("select distinct s from Staff s where s.externalUniqueId=:externalId")
                .setString("externalId", id).setCacheable(true).uniqueResult();
    }

    private void elementCourseCredit(Element element, CourseOffering co) throws Exception {
        String elementName = "courseCredit";
        Element credit = element.element(elementName);
        // if there is no credit on the course element, check the offering element instead
        if (credit == null)
            credit = element.getParent().element(elementName);
        if (credit != null) {
            String creditFormat = getRequiredStringAttribute(credit, "creditFormat", elementName);
            String creditType = getRequiredStringAttribute(credit, "creditType", elementName);
            String creditUnitType = getRequiredStringAttribute(credit, "creditUnitType", elementName);
            Boolean fractionalIncrementsAllowed = getOptionalBooleanAttribute(credit, "fractionalCreditAllowed",
                    true);

            String minCreditStr = getOptionalStringAttribute(credit, "fixedCredit");
            if (minCreditStr == null) {
                minCreditStr = getOptionalStringAttribute(credit, "minimumCredit");
            }
            Float minCredit = null;
            if (minCreditStr != null) {
                minCredit = new Float(minCreditStr);
            }
            String maxCreditStr = getOptionalStringAttribute(credit, "maximumCredit");
            Float maxCredit = null;
            if (maxCreditStr != null) {
                maxCredit = Float.parseFloat(maxCreditStr);
            }
            CourseCreditUnitConfig ccuc = CourseCreditUnitConfig.createCreditUnitConfigOfFormat(creditFormat,
                    creditType, creditUnitType, minCredit, maxCredit, fractionalIncrementsAllowed, true);
            if (ccuc != null) {
                co.setCredit(ccuc);
                ccuc.setOwner(co);
            }
        }
        return;
    }

    private boolean elementSubpartCredit(Element element, SchedulingSubpart ss) throws Exception {
        boolean changed = false;
        String elementName = "subpartCredit";
        Element credit = element.element(elementName);

        if (credit != null) {
            String creditFormat = getRequiredStringAttribute(credit, "creditFormat", elementName);
            String creditType = getRequiredStringAttribute(credit, "creditType", elementName);
            String creditUnitType = getRequiredStringAttribute(credit, "creditUnitType", elementName);
            Boolean fractionalIncrementsAllowed = getOptionalBooleanAttribute(credit, "fractionalCreditAllowed",
                    true);

            String minCreditStr = getOptionalStringAttribute(credit, "fixedCredit");
            if (minCreditStr == null) {
                minCreditStr = getOptionalStringAttribute(credit, "minimumCredit");
            }
            Float minCredit = null;
            if (minCreditStr != null) {
                minCredit = new Float(minCreditStr);
            }
            String maxCreditStr = getOptionalStringAttribute(credit, "maximumCredit");
            Float maxCredit = null;
            if (maxCreditStr != null) {
                maxCredit = Float.parseFloat(maxCreditStr);
            }
            CourseCreditUnitConfig ccuc = CourseCreditUnitConfig.createCreditUnitConfigOfFormat(creditFormat,
                    creditType, creditUnitType, minCredit, maxCredit, fractionalIncrementsAllowed, false);
            if (ss.getCredit() == null && ccuc != null) {
                addNote("\tadded subpart credit");
                changed = true;
            } else if (ss.getCredit() != null && !isSameCreditConfig(ss.getCredit(), ccuc)) {
                addNote("\tsubpart credit values changed");
                changed = true;
            }
            if (changed) {
                ss.setCredit(ccuc);
                ccuc.setOwner(ss);
                getHibSession().saveOrUpdate(ss);
                getHibSession().flush();
                getHibSession().refresh(ss);
                ChangeLog.addChange(getHibSession(), getManager(), session, ss.getCredit(),
                        ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.UPDATE,
                        ss.getControllingCourseOffering().getSubjectArea(),
                        ss.getControllingCourseOffering().getDepartment());
            }
        }
        return (changed);
    }

    private boolean handleDistributionPrefElement(Element element, Class_ c, String elementName,
            DistributionType distributionType) throws Exception {
        boolean changed = false;
        Vector<DistributionPref> existingDistPrefs = new Vector<DistributionPref>();
        if (c.getDistributionPreferences() != null) {
            for (Iterator<?> dpIt = c.getDistributionPreferences().iterator(); dpIt.hasNext();) {
                DistributionPref dp = (DistributionPref) dpIt.next();
                if (dp.getDistributionType().getUniqueId().equals(distributionType.getUniqueId())) {
                    existingDistPrefs.add(dp);
                }
            }
        }
        Vector<String> classIds = new Vector<String>();
        if (element.element(elementName) != null) {
            classIds.add(c.getExternalUniqueId());
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element distPrefElement = (Element) it.next();
                classIds.add(getRequiredStringAttribute(distPrefElement, "id", elementName));
            }

        }
        if (existingDistPrefs.size() != 1) {
            if (existingDistPrefs.size() > 1) {
                addNote("\tMultiple " + distributionType.getLabel()
                        + " distribution preferences exist -- deleted them");
                for (Iterator<?> dpIt = existingDistPrefs.iterator(); dpIt.hasNext();) {
                    DistributionPref dp = (DistributionPref) dpIt.next();
                    addNote("\t\tdeleted '" + dp.preferenceText() + "'");
                    deleteDistributionPref(dp);
                }
                changed = true;
            }
            if (classIds.size() > 1) {
                addDistributionPref(classIds, c, distributionType);
                changed = true;
            }
        } else {
            DistributionPref dp = (DistributionPref) existingDistPrefs.firstElement();
            if (classIds.size() > 1) {
                if (!isMatchingDistPref(dp, classIds)) {
                    changed = true;
                    deleteDistributionPref(dp);
                    addDistributionPref(classIds, c, distributionType);
                }

            } else {
                addNote("Class  " + c.getClassLabel() + " is no longer a " + distributionType.getLabel()
                        + ", removed" + dp.toString());
                deleteDistributionPref(dp);
                changed = true;
            }
        }
        return (changed);
    }

    private boolean elementCanShareRoom(Element element, Class_ c) throws Exception {
        if (!useCanShareRoomElement) {
            return (false);
        }
        return (handleDistributionPrefElement(element, c, "canShareRoom", canShareRoomType));
    }

    private boolean elementMeetsWith(Element element, Class_ c) throws Exception {
        if (!useMeetsWithElement) {
            return (false);
        }
        return (handleDistributionPrefElement(element, c, "meetsWith", meetsWithType));
    }

    private boolean isMatchingDistPref(DistributionPref dp, Vector<String> classExternalIds) {
        boolean isSame = false;
        DistributionObject distObj = null;
        String cei = null;
        boolean allFound = true;
        HashSet<DistributionObject> existingDistObjs = new HashSet<DistributionObject>();
        existingDistObjs.addAll(dp.getDistributionObjects());
        for (Iterator<?> ceiIt = classExternalIds.iterator(); ceiIt.hasNext();) {
            cei = (String) ceiIt.next();
            boolean found = false;
            for (Iterator<?> doIt = dp.getDistributionObjects().iterator(); doIt.hasNext();) {
                distObj = (DistributionObject) doIt.next();
                if (distObj.getPrefGroup() instanceof Class_) {
                    Class_ c = (Class_) distObj.getPrefGroup();
                    if (cei.equals(c.getExternalUniqueId())) {
                        found = true;
                        existingDistObjs.remove(distObj);
                        break;
                    }
                }
            }
            if (!found) {
                allFound = false;
                break;
            }
        }
        if (allFound && existingDistObjs.isEmpty()) {
            isSame = true;
        }
        return (isSame);
    }

    protected abstract boolean handleCustomCourseChildElements(CourseOffering courseOffering,
            Element courseOfferingElement) throws Exception;

    private boolean elementCourse(Element element, InstructionalOffering io, String action) throws Exception {
        boolean changed = false;
        if (io.getControllingCourseOffering() != null) {
            Debug.info("Checking Offering:  " + io.getCourseName());
        }
        ArrayList<ImportCourseOffering> courses = getCourses(element);
        if (action.equalsIgnoreCase("insert")) {
            for (Iterator<ImportCourseOffering> it = courses.iterator(); it.hasNext();) {
                ImportCourseOffering ico = it.next();
                CourseOffering co = ico.getCourseOffering();
                co.setInstructionalOffering(io);
                if (co.getNbrExpectedStudents() == null) {
                    co.setNbrExpectedStudents(new Integer(0));
                }
                if (co.getDemand() == null) {
                    co.setDemand(new Integer(0));
                }
                io.addTocourseOfferings(co);
                co.setPermId(InstrOfferingPermIdGenerator.getGenerator()
                        .generate((SessionImplementor) new CourseOfferingDAO().getSession(), this).toString());
                co.setSubjectAreaAbbv(co.getSubjectArea().getSubjectAreaAbbreviation());
                addNote("\tadded course: " + co.getSubjectArea().getSubjectAreaAbbreviation() + " "
                        + co.getCourseNbr());
                if (co.getCredit() != null)
                    addNote("\tadded credit: " + co.getCredit().creditAbbv());
                getHibSession().saveOrUpdate(io);
                getHibSession().flush();
                getHibSession().refresh(io);
                ChangeLog.addChange(getHibSession(), getManager(), session, co,
                        ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.CREATE, co.getSubjectArea(),
                        co.getDepartment());
                handleCustomCourseChildElements(co, ico.getElement());
            }
            changed = true;
        } else {
            CourseOffering nco = null;
            CourseOffering oco = null;
            for (Iterator<ImportCourseOffering> nit = courses.iterator(); nit.hasNext();) {
                ImportCourseOffering ico = (ImportCourseOffering) nit.next();
                nco = ico.getCourseOffering();
                boolean exists = false;
                for (Iterator<?> oit = io.getCourseOfferings().iterator(); oit.hasNext();) {
                    oco = (CourseOffering) oit.next();
                    if (isSameCourseOffering(oco, nco)) {
                        exists = true;
                        existingCourseOfferings.remove(oco.getUniqueId());
                        if (!oco.getSubjectArea().getUniqueId().equals(nco.getSubjectArea().getUniqueId())) {
                            oco.setSubjectArea(nco.getSubjectArea());
                            addNote("\tchanged subject area: " + nco.getSubjectArea().getSubjectAreaAbbreviation()
                                    + " " + nco.getCourseNbr());
                            changed = true;
                        }
                        if (!oco.getCourseNbr().equals(nco.getCourseNbr())) {
                            oco.setCourseNbr(nco.getCourseNbr());
                            addNote("\tchanged course number: " + nco.getSubjectArea().getSubjectAreaAbbreviation()
                                    + " " + nco.getCourseNbr());
                            changed = true;
                        }
                        if (!oco.isIsControl().equals(nco.getIsControl())) {
                            oco.setIsControl(nco.getIsControl());
                            addNote("\tchanged control flag: " + nco.getSubjectArea().getSubjectAreaAbbreviation()
                                    + " " + nco.getCourseNbr());
                            changed = true;
                        }
                        if (oco.getScheduleBookNote() == null && nco.getScheduleBookNote() != null) {
                            oco.setScheduleBookNote(nco.getScheduleBookNote());
                            addNote("\tadded schedule book note: "
                                    + nco.getSubjectArea().getSubjectAreaAbbreviation() + " " + nco.getCourseNbr());
                            changed = true;
                        } else if (oco.getScheduleBookNote() != null
                                && (nco.getScheduleBookNote() == null || nco.getScheduleBookNote().length() == 0)) {
                            oco.setScheduleBookNote(null);
                            addNote("\tremoved schedule book note: "
                                    + nco.getSubjectArea().getSubjectAreaAbbreviation() + " " + nco.getCourseNbr());
                            changed = true;
                        } else if (oco.getScheduleBookNote() != null && nco.getScheduleBookNote() != null
                                && !oco.getScheduleBookNote().equals(nco.getScheduleBookNote())) {
                            oco.setScheduleBookNote(nco.getScheduleBookNote());
                            addNote("\tchanged schedule book note: "
                                    + nco.getSubjectArea().getSubjectAreaAbbreviation() + " " + nco.getCourseNbr());
                            changed = true;
                        }
                        if (oco.getTitle() == null && nco.getTitle() != null && nco.getTitle().length() > 0) {
                            oco.setTitle(nco.getTitle());
                            addNote("\tadded title: " + nco.getSubjectArea().getSubjectAreaAbbreviation() + " "
                                    + nco.getCourseNbr());
                            changed = true;
                        } else if (oco.getTitle() != null
                                && (nco.getTitle() == null || nco.getTitle().length() == 0)) {
                            oco.setTitle(null);
                            addNote("\tremoved title: " + nco.getSubjectArea().getSubjectAreaAbbreviation() + " "
                                    + nco.getCourseNbr());
                            changed = true;
                        } else if (oco.getTitle() != null && nco.getTitle() != null
                                && !oco.getTitle().equals(nco.getTitle())) {
                            oco.setTitle(nco.getTitle());
                            addNote("\tchanged title: " + nco.getSubjectArea().getSubjectAreaAbbreviation() + " "
                                    + nco.getCourseNbr());
                            changed = true;
                        }
                        if (oco.getConsentType() == null && nco.getConsentType() != null) {
                            oco.setConsentType(nco.getConsentType());
                            addNote("\tadded consent: " + nco.getSubjectArea().getSubjectAreaAbbreviation() + " "
                                    + nco.getCourseNbr());
                            changed = true;
                        } else if (oco.getConsentType() != null && nco.getConsentType() == null) {
                            oco.setConsentType(null);
                            addNote("\tremoved consent: " + nco.getSubjectArea().getSubjectAreaAbbreviation() + " "
                                    + nco.getCourseNbr());
                            changed = true;
                        } else if (oco.getConsentType() != null && nco.getConsentType() != null
                                && !oco.getConsentType().equals(nco.getConsentType())) {
                            oco.setConsentType(nco.getConsentType());
                            addNote("\tchanged consent: " + nco.getSubjectArea().getSubjectAreaAbbreviation() + " "
                                    + nco.getCourseNbr());
                            changed = true;
                        }
                        if (oco.getCredit() == null && nco.getCredit() != null) {
                            CourseCreditUnitConfig credit = nco.getCredit();
                            oco.setCredit(credit);
                            credit.setOwner(oco);
                            addNote("\tadded credit: " + nco.getCredit().creditAbbv());
                            changed = true;
                        } else if (oco.getCredit() != null && nco.getCredit() == null) {
                            CourseCreditUnitConfig old = oco.getCredit();
                            getHibSession().delete(old);
                            oco.setCredit(null);
                            addNote("\tremoved credit: " + old);
                            changed = true;
                        } else if (oco.getCredit() != null
                                && !isSameCreditConfig(oco.getCredit(), nco.getCredit())) {
                            CourseCreditUnitConfig old = oco.getCredit();
                            getHibSession().delete(old);
                            CourseCreditUnitConfig credit = nco.getCredit();
                            oco.setCredit(credit);
                            credit.setOwner(oco);
                            addNote("\tchanged credit: " + oco.getCredit().creditAbbv());
                            changed = true;
                        }
                        if (oco.getPermId() == null) {
                            oco.setPermId(InstrOfferingPermIdGenerator.getGenerator()
                                    .generate((SessionImplementor) new CourseOfferingDAO().getSession(), this)
                                    .toString());
                            addNote("\tadded missing permId: " + nco.getSubjectArea().getSubjectAreaAbbreviation()
                                    + " " + nco.getCourseNbr());
                            changed = true;
                        }
                        if (handleCustomCourseChildElements(oco, ico.getElement())) {
                            changed = true;
                        }
                        if (changed) {
                            ChangeLog.addChange(getHibSession(), getManager(), session, oco,
                                    ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.UPDATE,
                                    oco.getSubjectArea(), oco.getDepartment());
                        }
                    }

                }
                if (!exists) {
                    addNote("\tmatching course offering not found, added new: "
                            + nco.getSubjectArea().getSubjectAreaAbbreviation() + " " + nco.getCourseNbr());
                    if (nco.getNbrExpectedStudents() == null) {
                        nco.setNbrExpectedStudents(new Integer(0));
                    }
                    if (nco.getDemand() == null) {
                        nco.setDemand(new Integer(0));
                    }
                    nco.setSubjectAreaAbbv(nco.getSubjectArea().getSubjectAreaAbbreviation());
                    nco.setInstructionalOffering(io);
                    if (nco.getCredit() != null)
                        addNote("\tadded credit: " + nco.getCredit().creditAbbv());
                    io.addTocourseOfferings(nco);
                    changed = true;
                    getHibSession().saveOrUpdate(io);
                    getHibSession().flush();
                    getHibSession().refresh(io);
                    handleCustomCourseChildElements(nco, ico.getElement());
                    ChangeLog.addChange(getHibSession(), getManager(), session, nco,
                            ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.CREATE,
                            nco.getSubjectArea(), nco.getDepartment());
                }
            }
            List removeCourses = new ArrayList<Object>();
            removeCourses.addAll(io.getCourseOfferings());
            for (Iterator<?> coIt = removeCourses.iterator(); coIt.hasNext();) {
                CourseOffering co = (CourseOffering) coIt.next();
                if (co.getUniqueId() != null && existingCourseOfferings.contains(co.getUniqueId())) {
                    addNote("\tremoved course offering from instructional offering: " + co.getCourseName());
                    deleteCourseOffering(co);
                    changed = true;
                }
            }
            boolean hasControl = false;
            for (Iterator<?> coIt = io.getCourseOfferings().iterator(); coIt.hasNext();) {
                CourseOffering co = (CourseOffering) coIt.next();
                if (co.isIsControl().booleanValue()) {
                    hasControl = true;
                }
            }
            if (!hasControl) {
                throw new Exception("Expected 'offering' to have a course marked as control.");
            }
        }

        return (changed);

    }

    private boolean elementInstrOffrConfig(Element element, InstructionalOffering io) throws Exception {
        boolean changed = false;
        HashMap<String, InstrOfferingConfig> existingConfigs = new HashMap<String, InstrOfferingConfig>();
        if (io.getInstrOfferingConfigs() != null) {
            for (Iterator<?> it = io.getInstrOfferingConfigs().iterator(); it.hasNext();) {
                InstrOfferingConfig ioc = (InstrOfferingConfig) it.next();
                existingConfigs.put(ioc.getName(), ioc);
            }
        }

        String elementName = "config";
        if (element.element(elementName) != null) {
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element configElement = (Element) it.next();
                boolean addNew = false;
                String name = getRequiredStringAttribute(configElement, "name", elementName);
                String limitStr = getRequiredStringAttribute(configElement, "limit", elementName);
                Integer limit = new Integer(0);
                Boolean unlimited = new Boolean(false);
                if (limitStr.equalsIgnoreCase("inf")) {
                    unlimited = new Boolean(true);
                } else {
                    limit = Integer.valueOf(limitStr);
                }

                InstrOfferingConfig ioc = null;
                if (existingConfigs.containsKey(name)) {
                    ioc = (InstrOfferingConfig) existingConfigs.get(name);
                    existingConfigs.remove(name);
                } else {
                    addNote("\tdid not find matching config element, adding new config: " + name);
                    ioc = new InstrOfferingConfig();
                    ioc.setName(name);
                    ioc.setInstructionalOffering(io);
                    io.addToinstrOfferingConfigs(ioc);
                    changed = true;
                    addNew = true;
                }
                if (ioc.getLimit() == null || !ioc.getLimit().equals(limit)) {
                    addNote("\tconfig limit changed");
                    ioc.setLimit(limit);
                    changed = true;
                }
                if (ioc.isUnlimitedEnrollment() == null || !ioc.isUnlimitedEnrollment().equals(unlimited)) {
                    addNote("\tconfig unlimited changed");
                    ioc.setUnlimitedEnrollment(unlimited);
                    changed = true;
                }

                if (changed) {
                    this.getHibSession().saveOrUpdate(ioc);
                }

                if (handleCustomInstrOffrConfigChildElements(ioc, configElement)) {
                    addNote("\tconfig changed by custom element");
                    changed = true;
                }
                if (elementSubpart(configElement, ioc, null, null)) {
                    addNote("\tconfig subparts changed");
                    changed = true;
                }
                if (changed) {
                    this.getHibSession().saveOrUpdate(ioc);
                }

                if (elementClass(configElement, ioc, null, null)) {
                    addNote("\tconfig classes changed");
                    changed = true;
                }

                if (changed) {
                    addNote("\tconfig element changed: " + name);
                    ChangeLog.addChange(getHibSession(), getManager(), session, ioc,
                            ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                            (addNew ? ChangeLog.Operation.CREATE : ChangeLog.Operation.UPDATE),
                            ioc.getControllingCourseOffering().getSubjectArea(),
                            ioc.getControllingCourseOffering().getDepartment());
                    this.getHibSession().saveOrUpdate(ioc);
                }
            }
        }

        if (existingConfigs.size() > 0) {
            for (Iterator<InstrOfferingConfig> cIt = existingConfigs.values().iterator(); cIt.hasNext();) {
                InstrOfferingConfig ioc = (InstrOfferingConfig) cIt.next();
                deleteInstrOffrConfig(ioc);
            }
            changed = true;
        }
        return (changed);
    }

    private boolean elementClass(Element element, InstrOfferingConfig ioc, Class_ parentClass,
            HashSet<Class_> allExistingClasses) throws Exception {
        boolean changed = false;
        HashMap<String, Class_> possibleClassesAtThisLevel = new HashMap<String, Class_>();
        ArrayList<SchedulingSubpart> possibleSubpartsAtThisLevel = new ArrayList<SchedulingSubpart>();
        if (parentClass == null) {
            allExistingClasses = new HashSet<Class_>();
            if (ioc.getSchedulingSubparts() != null) {
                for (Iterator<?> ssIt = ioc.getSchedulingSubparts().iterator(); ssIt.hasNext();) {
                    SchedulingSubpart ss = (SchedulingSubpart) ssIt.next();
                    if (ss.getClasses() != null) {
                        for (Iterator<?> cIt = ss.getClasses().iterator(); cIt.hasNext();) {
                            Class_ c = (Class_) cIt.next();
                            allExistingClasses.add(c);
                            if (c.getParentClass() == null) {
                                if (c.getExternalUniqueId() != null) {
                                    possibleClassesAtThisLevel.put(c.getExternalUniqueId(), c);
                                } else {
                                    possibleClassesAtThisLevel
                                            .put(c.getSchedulingSubpart().getItype().getAbbv().trim()
                                                    + c.getClassSuffix(), c);
                                }
                            }
                        }
                    }
                    if (ss.getParentSubpart() == null) {
                        possibleSubpartsAtThisLevel.add(ss);
                    }
                }
            }
        } else {
            if (parentClass.getChildClasses() != null) {
                for (Iterator<?> it = parentClass.getChildClasses().iterator(); it.hasNext();) {
                    Class_ c = (Class_) it.next();
                    if (c.getExternalUniqueId() != null) {
                        possibleClassesAtThisLevel.put(c.getExternalUniqueId(), c);
                    } else {
                        possibleClassesAtThisLevel
                                .put(c.getSchedulingSubpart().getItype().getAbbv().trim() + c.getClassSuffix(), c);
                    }
                }
            }
            if (parentClass.getSchedulingSubpart().getChildSubparts() != null) {
                possibleSubpartsAtThisLevel.addAll(parentClass.getSchedulingSubpart().getChildSubparts());
            }
        }

        String elementName = "class";
        if (element.element(elementName) != null) {
            if (parentClass == null
                    && (ioc.getSchedulingSubparts() == null || ioc.getSchedulingSubparts().isEmpty())) {
                throw new Exception(ioc.getCourseName()
                        + " - If a 'config' has 'class' elements it must also have matching 'subpart' elements");
            }
            for (Iterator<?> cIt = element.elementIterator(elementName); cIt.hasNext();) {
                Element classElement = (Element) cIt.next();
                boolean isAdd = false;
                String id = getOptionalStringAttribute(classElement, "id");
                String managingDeptStr = getOptionalStringAttribute(classElement, "managingDept");
                Department managingDept = null;
                if (managingDeptStr != null && managingDeptStr.trim().length() > 0) {
                    managingDept = Department.findByDeptCode(managingDeptStr.trim(), session.getUniqueId());
                }
                String limitStr = getRequiredStringAttribute(classElement, "limit", elementName);
                Integer limit = new Integer(0);
                if (!limitStr.equalsIgnoreCase("inf")) {
                    limit = Integer.valueOf(limitStr);
                }
                String suffix = getRequiredStringAttribute(classElement, "suffix", elementName);
                String type = getRequiredStringAttribute(classElement, "type", elementName);
                String scheduleNote = getOptionalStringAttribute(classElement, "scheduleNote");
                Boolean enabledForStudentScheduling = getOptionalBooleanAttribute(classElement, "studentScheduling",
                        getOptionalBooleanAttribute(classElement, "displayInScheduleBook", true));
                Integer itypeId = findItypeForString(type).getItype();

                Class_ clazz = null;
                Class_ origClass = null;
                if (id != null) {
                    origClass = (Class_) possibleClassesAtThisLevel.get(id);
                    if (origClass != null) {
                        possibleClassesAtThisLevel.remove(id);
                        if (!origClass.getClassSuffix().equals(suffix)) {
                            changed = true;
                            origClass.setClassSuffix(suffix);
                            Integer origSectionNbr = origClass.getSectionNumberCache();
                            try {
                                origClass.setSectionNumberCache(new Integer(suffix));
                            } catch (Exception e) {
                                origClass.setSectionNumberCache(origSectionNbr);
                            }
                            addNote("\t suffix for class changed: " + origClass.getClassLabel());

                        }
                    }
                }
                if (origClass == null) {
                    origClass = (Class_) possibleClassesAtThisLevel.get(type + suffix);
                    if (origClass != null && origClass.getExternalUniqueId() != null && id != null
                            && !origClass.getExternalUniqueId().equals(id)) {
                        origClass = null;
                    } else if (origClass != null) {
                        possibleClassesAtThisLevel.remove(type + suffix);
                    }
                }
                if (origClass != null) {
                    clazz = origClass;
                    allExistingClasses.remove(origClass);
                    existingClasses.remove(clazz.getUniqueId());
                    SchedulingSubpart prevSubpart = clazz.getSchedulingSubpart();
                    if (!clazz.getSchedulingSubpart().getItype().getItype().equals(itypeId)
                            || !possibleSubpartsAtThisLevel.contains(clazz.getSchedulingSubpart())) {
                        for (Iterator<SchedulingSubpart> ssIt = possibleSubpartsAtThisLevel.iterator(); ssIt
                                .hasNext();) {
                            SchedulingSubpart ss = (SchedulingSubpart) ssIt.next();
                            if (ss.getItype().getItype().equals(itypeId)) {
                                org.hibernate.Session hSess = this.getHibSession();
                                clazz.setSchedulingSubpart(ss);
                                ss.addToclasses(clazz);
                                hSess.update(ss);
                                hSess.flush();
                                hSess.refresh(ss);
                                hSess.refresh(prevSubpart);
                                break;
                            }
                        }
                        prevSubpart.getClasses().remove(clazz);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix + " 'class' itype changed");
                        changed = true;
                    }
                    if ((clazz.getSchedulePrintNote() != null && !clazz.getSchedulePrintNote().equals(scheduleNote))
                            || (clazz.getSchedulePrintNote() == null && scheduleNote != null)) {
                        clazz.setSchedulePrintNote(scheduleNote);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' schedule note changed");
                        changed = true;
                    }
                    if ((clazz.getExpectedCapacity() != null && !clazz.getExpectedCapacity().equals(limit))
                            || (clazz.getExpectedCapacity() == null && limit != null)) {
                        clazz.setExpectedCapacity(limit);
                        clazz.setMaxExpectedCapacity(limit);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix + " 'class' limit changed");
                        changed = true;
                    }
                    if ((clazz.getManagingDept() != null && managingDept != null
                            && !clazz.getManagingDept().getUniqueId().equals(managingDept.getUniqueId()))
                            || (clazz.getManagingDept() == null && managingDept != null)) {
                        clazz.setManagingDept(managingDept);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' managing department changed");
                        changed = true;
                    }
                    if (!enabledForStudentScheduling.equals(clazz.isEnabledForStudentScheduling())) {
                        clazz.setEnabledForStudentScheduling(enabledForStudentScheduling);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' display in schedule book changed");
                        changed = true;
                    }
                } else {
                    isAdd = true;
                    clazz = new Class_();
                    clazz.setExternalUniqueId(id);
                    clazz.setClassSuffix(suffix);
                    try {
                        clazz.setSectionNumberCache(new Integer(suffix));
                    } catch (Exception e) {
                        // Ignore Exception                  
                    }

                    clazz.setExpectedCapacity(limit);
                    clazz.setMaxExpectedCapacity(limit);
                    clazz.setRoomRatio(new Float(1.0));
                    clazz.setNbrRooms(new Integer(1));
                    clazz.setEnabledForStudentScheduling(enabledForStudentScheduling);
                    clazz.setSchedulePrintNote(scheduleNote);
                    clazz.setDisplayInstructor(new Boolean(true));
                    if (managingDept != null) {
                        clazz.setManagingDept(managingDept);
                    }
                    if (parentClass != null) {
                        clazz.setParentClass(parentClass);
                        parentClass.addTochildClasses(clazz);
                    }
                    for (Iterator<SchedulingSubpart> ssIt = possibleSubpartsAtThisLevel.iterator(); ssIt
                            .hasNext();) {
                        SchedulingSubpart ss = (SchedulingSubpart) ssIt.next();
                        if (ss.getItype().getItype().equals(itypeId)) {
                            clazz.setSchedulingSubpart(ss);
                            ss.addToclasses(clazz);
                            break;
                        }
                    }
                    addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                            + " 'class' matching class not found adding new class");
                    changed = true;
                }
                if (clazz.getSchedulingSubpart() == null) {
                    throw new Exception(ioc.getCourseName() + " " + type + " " + suffix
                            + " 'class' does not have matching 'subpart'");
                }

                if (elementInstructor(classElement, clazz)) {
                    addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                            + " 'class' instructor data changed");
                    changed = true;
                }

                DatePattern dp = null;
                if (classElement.element("time") != null
                        && classElement.element("time").attributeValue("datePattern") != null) {
                    dp = DatePattern.findByName(session,
                            classElement.element("time").attributeValue("datePattern"));
                }
                HashMap<String, Vector<Calendar>> dates = elementDates(classElement);
                if (dp == null && dates != null) {
                    dp = findDatePattern(dates.get("startDates"), dates.get("endDates"), clazz);
                }
                if (classElement.element("meeting") == null) {
                    if (dp == null && clazz.getDatePattern() != null) {
                        if (!clazz.getDatePattern().isDefault()
                                && clazz.getSchedulingSubpart().effectiveDatePattern().isDefault()) {
                            clazz.setDatePattern(dp);
                            addNote("\t" + ioc.getCourseName() + " " + type + suffix
                                    + " 'class' date pattern changed back to default");
                            changed = true;
                        } else if (!clazz.getDatePattern().isDefault()
                                && !clazz.getSchedulingSubpart().effectiveDatePattern().isDefault()) {
                            clazz.setDatePattern(session.getDefaultDatePatternNotNull());
                            addNote("\t" + ioc.getCourseName() + " " + type + suffix
                                    + " 'class' date pattern changed to default");
                            changed = true;
                        }
                    } else if (dp != null && clazz.getDatePattern() == null) {
                        clazz.setDatePattern(dp);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + "'class' date pattern changed from default");
                        changed = true;
                    } else if (dp != null && clazz.getDatePattern() != null
                            && !clazz.getDatePattern().getUniqueId().equals(dp.getUniqueId())) {
                        clazz.setDatePattern(dp);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' date pattern changed");
                        changed = true;
                    }
                }
                if (changed) {
                    this.getHibSession().saveOrUpdate(clazz);
                }

                if (elementMeetsWith(classElement, clazz)) {
                    addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                            + " 'class' meets with preferences changed");
                    changed = true;
                }

                if (elementCanShareRoom(classElement, clazz)) {
                    addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                            + " 'class' can share room preferences changed");
                    changed = true;
                }

                if (classElement.element("meeting") != null) {
                    if (elementMeetings(classElement, clazz)) {
                        changed = true;
                    }
                    int numRooms = 1;
                    if (clazz.getNbrRooms() != null && !clazz.getNbrRooms().equals(new Integer(numRooms))) {
                        clazz.setNbrRooms(new Integer(numRooms));
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " number of rooms changed");
                        changed = true;
                    }

                } else if (classElement.element("time") != null) {
                    TimeObject meetingTime = elementTime(classElement);
                    Vector<Room> rooms = elementRoom(classElement, clazz);
                    Vector<NonUniversityLocation> locations = elementLocation(classElement, clazz);
                    int numRooms = 0;
                    if (rooms != null && !rooms.isEmpty()) {
                        numRooms += rooms.size();
                    }
                    if (locations != null && !locations.isEmpty()) {
                        numRooms += locations.size();
                    }
                    if (clazz.getNbrRooms() != null && !clazz.getNbrRooms().equals(new Integer(numRooms))) {
                        clazz.setNbrRooms(new Integer(numRooms));
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " number of rooms changed");
                        changed = true;
                    }
                    if (addUpdateClassEvent(clazz, meetingTime, rooms, locations)) {
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' events for class changed");
                        changed = true;
                    }
                    TimePattern tp = findTimePatternForMeetingInfo(clazz, meetingTime);
                    if (tp != null && clazz.getTimePatterns() != null && !clazz.getTimePatterns().contains(tp)) {
                        for (Iterator it = clazz.getTimePreferences().iterator(); it.hasNext();) {
                            TimePref pref = (TimePref) it.next();
                            clazz.getPreferences().remove(pref);
                        }
                        TimePref tpref = new TimePref();
                        tpref.setTimePattern(tp);
                        tpref.setOwner(clazz);
                        tpref.setPrefLevel(requiredPrefLevel);
                        clazz.addTopreferences(tpref);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' time pattern for class changed");
                        changed = true;
                    } else if (tp != null
                            && (clazz.getTimePatterns() == null || clazz.getTimePatterns().isEmpty())) {
                        TimePref tpref = new TimePref();
                        tpref.setTimePattern(tp);
                        tpref.setOwner(clazz);
                        tpref.setPrefLevel(requiredPrefLevel);
                        clazz.addTopreferences(tpref);
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' time pattern for class added");
                        changed = true;
                    } else if (tp == null && clazz.getTimePatterns() != null
                            && !clazz.getTimePatterns().isEmpty()) {
                        for (Iterator it = clazz.getTimePreferences().iterator(); it.hasNext();) {
                            TimePref pref = (TimePref) it.next();
                            clazz.getPreferences().remove(pref);
                        }
                        addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                                + " 'class' time pattern for class removed");
                        changed = true;
                    }
                }
                if (handleCustomClassChildElements(classElement, ioc, clazz)) {
                    changed = true;
                }
                if (elementClass(classElement, ioc, clazz, allExistingClasses)) {
                    addNote("\t" + ioc.getCourseName() + " " + type + " " + suffix
                            + " 'class' child classes changed");
                    changed = true;
                }
                if (changed) {
                    getHibSession().saveOrUpdate(clazz);
                    getHibSession().flush();
                    getHibSession().refresh(clazz);
                }
                if (changed) {
                    ChangeLog.addChange(getHibSession(), getManager(), session, clazz,
                            ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                            (isAdd ? ChangeLog.Operation.CREATE : ChangeLog.Operation.UPDATE),
                            ioc.getControllingCourseOffering().getSubjectArea(), ioc.getDepartment());
                }
            }
        }

        if (possibleClassesAtThisLevel.size() > 0) {
            addNote("\t" + ioc.getCourseName() + " 'class' not all classes at this level had matches");
            for (Iterator<Class_> cIt = possibleClassesAtThisLevel.values().iterator(); cIt.hasNext();) {
                Class_ c = (Class_) cIt.next();
                if (c.getParentClass() != null && c.getParentClass().equals(parentClass)) {
                    parentClass.getChildClasses().remove(c);
                    c.setParentClass(null);
                }
            }
            changed = true;
        }

        if (parentClass == null && allExistingClasses.size() > 0) {
            info(ioc.getCourseName() + " 'class' not all classes had matches, removing those without matches");
            for (Iterator<Class_> cIt = allExistingClasses.iterator(); cIt.hasNext();) {
                Class_ c = (Class_) cIt.next();
                deleteClass(c);
            }
        }
        return changed;
    }

    private TimePattern findTimePatternForMeetingInfo(Class_ clazz, TimeObject timeObject) {
        int days = 0;
        for (Integer dayOfWeek : timeObject.getDays()) {
            if (dayOfWeek == Calendar.MONDAY) {
                days += Constants.DAY_CODES[Constants.DAY_MON];
            } else if (dayOfWeek == Calendar.TUESDAY) {
                days += Constants.DAY_CODES[Constants.DAY_TUE];
            } else if (dayOfWeek == Calendar.WEDNESDAY) {
                days += Constants.DAY_CODES[Constants.DAY_WED];
            } else if (dayOfWeek == Calendar.THURSDAY) {
                days += Constants.DAY_CODES[Constants.DAY_THU];
            } else if (dayOfWeek == Calendar.FRIDAY) {
                days += Constants.DAY_CODES[Constants.DAY_FRI];
            } else if (dayOfWeek == Calendar.SATURDAY) {
                days += Constants.DAY_CODES[Constants.DAY_SAT];
            } else if (dayOfWeek == Calendar.SUNDAY) {
                days += Constants.DAY_CODES[Constants.DAY_SUN];
            }
        }
        String timePatternLookupString = days + "x"
                + (clazz.getSchedulingSubpart().getMinutesPerWk() / timeObject.getDays().size() + "x"
                        + timeObject.getStartPeriod().toString());
        return (timePatterns.get(timePatternLookupString));
    }

    protected abstract boolean handleCustomClassChildElements(Element classElement, InstrOfferingConfig ioc,
            Class_ clazz) throws Exception;

    protected abstract boolean handleCustomInstrOffrConfigChildElements(InstrOfferingConfig instrOfferingConfig,
            Element instrOfferingConfigElement) throws Exception;

    private Vector<Meeting> getMeetings(Date startDate, Date stopDate, String pattern, TimeObject meetingTime,
            Vector<Room> rooms, Vector<NonUniversityLocation> locations) {
        if (meetingTime != null) {
            Meeting meeting = meetingTime.asMeeting();
            meeting.setStatus(Meeting.Status.APPROVED);
            meeting.setApprovalDate(Calendar.getInstance().getTime());

            Calendar startDateCal = Calendar.getInstance();
            startDateCal.setTime(startDate);
            Calendar stopDateCal = Calendar.getInstance();
            stopDateCal.setTime(stopDate);
            int index = 0;
            Vector<Meeting> meetingsForDates = new Vector<Meeting>();
            while (!startDateCal.after(stopDateCal)) {
                if (meetingTime.getDays().contains(startDateCal.get(Calendar.DAY_OF_WEEK))
                        && pattern.charAt(index) == '1') {
                    Meeting dateMeeting = (Meeting) meeting.clone();
                    dateMeeting.setMeetingDate(startDateCal.getTime());
                    meetingsForDates.add(dateMeeting);
                }
                index++;
                startDateCal.add(Calendar.DAY_OF_MONTH, 1);
            }

            if ((rooms == null || rooms.isEmpty()) && (locations == null || locations.isEmpty())) {
                return (meetingsForDates);
            }

            Vector<Meeting> meetingsForLocations = new Vector<Meeting>();
            if (rooms != null) {
                for (Iterator<Room> rIt = rooms.iterator(); rIt.hasNext();) {
                    Room r = (Room) rIt.next();
                    for (Iterator<Meeting> dmIt = meetingsForDates.iterator(); dmIt.hasNext();) {
                        Meeting dateMeeting = (Meeting) dmIt.next();
                        Meeting roomMeeting = (Meeting) dateMeeting.clone();
                        roomMeeting.setLocationPermanentId(r.getPermanentId());
                        meetingsForLocations.add(roomMeeting);
                    }
                }
            }
            if (locations != null) {
                for (Iterator<NonUniversityLocation> rIt = locations.iterator(); rIt.hasNext();) {
                    NonUniversityLocation nul = (NonUniversityLocation) rIt.next();
                    for (Iterator<Meeting> dmIt = meetingsForDates.iterator(); dmIt.hasNext();) {
                        Meeting dateMeeting = (Meeting) dmIt.next();
                        Meeting roomMeeting = (Meeting) dateMeeting.clone();
                        roomMeeting.setLocationPermanentId(nul.getPermanentId());
                        meetingsForLocations.add(roomMeeting);
                    }
                }

            }
            if (!meetingsForLocations.isEmpty()) {
                return (meetingsForLocations);
            }
        }
        return (null);

    }

    private Vector<Meeting> getMeetings(DatePattern dp, TimeObject meetingTime, Vector<Room> rooms,
            Vector<NonUniversityLocation> locations) {
        return (getMeetings(dp.getStartDate(), dp.getEndDate(), dp.getPattern(), meetingTime, rooms, locations));
    }

    private void addDistributionPref(Vector<String> classIds, Class_ clazz, DistributionType distributionType)
            throws Exception {
        if (classIds.size() <= 1) {
            throw (new Exception("There must be at least two classes to have a meets with distribution preference: "
                    + clazz.getClassLabel()));
        }
        Class_ c = null;
        Vector<String> tmpClassIds = new Vector<String>();
        tmpClassIds.addAll(classIds);
        Vector<Class_> classes = new Vector<Class_>();
        String externalId = null;
        for (Iterator<?> eIt = classIds.iterator(); eIt.hasNext();) {
            externalId = (String) eIt.next();
            if (externalId.equals(clazz.getExternalUniqueId())) {
                classes.add(clazz);
                tmpClassIds.remove(externalId);
            } else {
                c = findClassForExternalUniqueId(externalId);
                if (c == null) {
                    break;
                } else {
                    classes.add(c);
                    tmpClassIds.remove(externalId);
                }
            }
        }
        if (!tmpClassIds.isEmpty()) {
            addNote("\t not all classes for this meets with pref exist yet, will add it later:"
                    + clazz.getClassLabel());
        } else {
            DistributionPref dp = new DistributionPref();
            dp.setDistributionType(distributionType);
            dp.setGrouping(DistributionPref.sGroupingNone);
            dp.setPrefLevel(requiredPrefLevel);
            dp.setOwner(clazz.getSchedulingSubpart().getControllingDept());
            for (Iterator<Class_> cIt = classes.iterator(); cIt.hasNext();) {
                c = (Class_) cIt.next();
                DistributionObject distObj = new DistributionObject();
                distObj.setDistributionPref(dp);
                distObj.setPrefGroup(c);
                dp.addTodistributionObjects(distObj);
                c.addTodistributionObjects(distObj);
            }
            getHibSession().save(dp);
            getHibSession().flush();
            getHibSession().refresh(dp);
            ChangeLog.addChange(getHibSession(), getManager(), session, dp, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                    ChangeLog.Operation.CREATE, null, clazz.getSchedulingSubpart().getControllingDept());
        }

    }

    private boolean addUpdateClassEvent(Class_ c, TimeObject meetingTime, Vector<Room> rooms,
            Vector<NonUniversityLocation> locations) {
        return (addUpdateClassEvent(c, getMeetings(c.effectiveDatePattern(), meetingTime, rooms, locations)));
    }

    private boolean addUpdateClassEvent(Class_ c, Vector<Meeting> meetings) {
        boolean changed = false;
        Date approvedTime = new Date();
        ClassEvent origEvent = c.getEvent();
        if (meetings != null && !meetings.isEmpty() && origEvent == null) {
            ClassEvent newEvent = new ClassEvent();
            newEvent.setClazz(c);
            c.setEvent(newEvent);
            newEvent.setMaxCapacity(c.getMaxExpectedCapacity());
            newEvent.setMinCapacity(c.getExpectedCapacity());
            newEvent.setEventName(c.getSchedulingSubpart().getInstrOfferingConfig().getCourseName() + " "
                    + c.getSchedulingSubpart().getItype().getAbbv().trim() + " " + c.getClassSuffix());
            for (Iterator<Meeting> mIt = meetings.iterator(); mIt.hasNext();) {
                Meeting meeting = (Meeting) mIt.next();
                meeting.setEvent(newEvent);
                meeting.setStatus(Meeting.Status.APPROVED);
                meeting.setApprovalDate(approvedTime);
                newEvent.addTomeetings(meeting);
            }
            getHibSession().save(newEvent);
            assignmentHelper.createAssignment(newEvent);
            changed = true;
            addNote("\tdid not find matching event, added new event: "
                    + c.getSchedulingSubpart().getInstrOfferingConfig().getCourseName() + " "
                    + c.getSchedulingSubpart().getItype().getAbbv().trim() + " " + c.getClassSuffix());
            ChangeLog.addChange(getHibSession(), getManager(), session, newEvent,
                    ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.CREATE,
                    c.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                    c.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
        } else if (origEvent != null) {
            if (!origEvent.getEventName().equals(c.getSchedulingSubpart().getInstrOfferingConfig().getCourseName()
                    + " " + c.getSchedulingSubpart().getItype().getAbbv().trim() + " " + c.getClassSuffix())) {
                origEvent.setEventName(c.getSchedulingSubpart().getInstrOfferingConfig().getCourseName() + " "
                        + c.getSchedulingSubpart().getItype().getAbbv().trim() + " " + c.getClassSuffix());
                changed = true;
                addNote("\tevent name changed");
            }
            if (origEvent.getMinCapacity() != null && c.getExpectedCapacity() != null
                    && !origEvent.getMinCapacity().equals(c.getExpectedCapacity())
                    || (origEvent.getMinCapacity() != null && c.getExpectedCapacity() == null)
                    || (origEvent.getMinCapacity() == null && c.getExpectedCapacity() != null)) {
                origEvent.setMinCapacity(c.getExpectedCapacity());
                changed = true;
                addNote("\tevent minimum capacity changed.");
            }
            if (origEvent.getMaxCapacity() != null && c.getMaxExpectedCapacity() != null
                    && !origEvent.getMaxCapacity().equals(c.getMaxExpectedCapacity())
                    || (origEvent.getMaxCapacity() != null && c.getMaxExpectedCapacity() == null)
                    || (origEvent.getMaxCapacity() == null && c.getMaxExpectedCapacity() != null)) {
                origEvent.setMaxCapacity(c.getMaxExpectedCapacity());
                changed = true;
                addNote("\tevent maximum capacity changed.");
            }
            Set origMeetings = new TreeSet<Object>();
            origMeetings.addAll(origEvent.getMeetings());
            if (meetings != null) {
                for (Iterator<Meeting> nmIt = meetings.iterator(); nmIt.hasNext();) {
                    Meeting newMeeting = (Meeting) nmIt.next();
                    boolean found = false;
                    for (Iterator<?> omIt = origMeetings.iterator(); omIt.hasNext();) {
                        Meeting origMeeting = (Meeting) omIt.next();
                        if (isSameMeeting(origMeeting, newMeeting)) {
                            found = true;
                            origMeetings.remove(origMeeting);
                            break;
                        }
                    }
                    if (!found) {
                        addNote("\tdid not find matching meeting, adding new meeting to event: "
                                + c.getClassLabel());
                        newMeeting.setEvent(origEvent);
                        newMeeting.setStatus(Meeting.Status.APPROVED);
                        newMeeting.setApprovalDate(approvedTime);
                        origEvent.addTomeetings(newMeeting);
                        changed = true;
                    }
                }
            }
            if (!origMeetings.isEmpty()) {
                addNote("\tsome existing meetings did not have matches in input, deleted them: "
                        + c.getClassLabel());
                for (Iterator<?> mIt = origMeetings.iterator(); mIt.hasNext();) {
                    Meeting m = (Meeting) mIt.next();
                    origEvent.getMeetings().remove(m);
                    m.setEvent(null);
                    ChangeLog.addChange(getHibSession(), getManager(), session, m,
                            ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.DELETE,
                            c.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                            c.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
                    getHibSession().delete(m);
                    changed = true;
                }
            }
            if (changed) {
                assignmentHelper.createAssignment(origEvent);
                ChangeLog.addChange(getHibSession(), getManager(), session, origEvent,
                        ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.UPDATE,
                        c.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                        c.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
                getHibSession().update(origEvent);
            }
        }

        return (changed);
    }

    private boolean elementSubpart(Element element, InstrOfferingConfig ioc, SchedulingSubpart parentSubpart,
            HashSet<SchedulingSubpart> allExistingSubparts) throws Exception {
        boolean changed = false;
        HashMap<String, SchedulingSubpart> thisLevelSubparts = new HashMap<String, SchedulingSubpart>();
        if (parentSubpart == null) {
            allExistingSubparts = new HashSet<SchedulingSubpart>();
            if (ioc.getSchedulingSubparts() != null) {
                for (Iterator<?> it = ioc.getSchedulingSubparts().iterator(); it.hasNext();) {
                    SchedulingSubpart ss = (SchedulingSubpart) it.next();
                    allExistingSubparts.add(ss);
                    if (ss.getParentSubpart() == null) {
                        thisLevelSubparts.put(
                                ss.getItype().getAbbv().trim() + (ss.getSchedulingSubpartSuffixCache() == null ? ""
                                        : (ss.getSchedulingSubpartSuffixCache().equals("-") ? ""
                                                : ss.getSchedulingSubpartSuffixCache())),
                                ss);
                    }
                }
            }
        } else {
            if (parentSubpart.getChildSubparts() != null) {
                for (Iterator<?> it = parentSubpart.getChildSubparts().iterator(); it.hasNext();) {
                    SchedulingSubpart ss = (SchedulingSubpart) it.next();
                    thisLevelSubparts
                            .put(ss.getItype().getAbbv().trim() + (ss.getSchedulingSubpartSuffixCache() == null ? ""
                                    : (ss.getSchedulingSubpartSuffixCache().equals("-") ? ""
                                            : ss.getSchedulingSubpartSuffixCache())),
                                    ss);
                }
            }
        }

        String elementName = "subpart";
        if (element.element(elementName) != null) {
            for (Iterator<?> it = element.elementIterator(elementName); it.hasNext();) {
                Element subpart = (Element) it.next();
                boolean isAdd = false;
                Integer minPerWeek = getOptionalIntegerAttribute(subpart, "minPerWeek");
                if (minPerWeek == null) {
                    minPerWeek = new Integer(0);
                }
                String typeStr = getRequiredStringAttribute(subpart, "type", elementName);
                ItypeDesc itype = findItypeForString(typeStr);
                String type = "";
                if (itype != null) {
                    type = itype.getAbbv().trim();
                }
                String suffix = getOptionalStringAttribute(subpart, "suffix");
                if (suffix != null) {
                    suffix = suffix.trim();
                } else {
                    suffix = "";
                }
                SchedulingSubpart ss = null;
                if (thisLevelSubparts.containsKey(type + suffix)) {
                    ss = (SchedulingSubpart) thisLevelSubparts.get(type + suffix);
                    allExistingSubparts.remove(ss);
                    if (thisLevelSubparts.containsKey(type + suffix)) {
                        thisLevelSubparts.remove(type + suffix);
                    }
                } else if (parentSubpart != null && parentSubpart.getItype().getAbbv().trim().equals(type)
                        && suffix.length() == 0 && thisLevelSubparts.containsKey(type + "a")) {
                    ss = (SchedulingSubpart) thisLevelSubparts.get(type + "a");
                    allExistingSubparts.remove(ss);
                    if (thisLevelSubparts.containsKey(type + "a")) {
                        thisLevelSubparts.remove(type + "a");
                    }
                } else {
                    ss = new SchedulingSubpart();
                    ss.setItype(itype);
                    ss.setSchedulingSubpartSuffixCache(suffix);
                    ss.setInstrOfferingConfig(ioc);
                    ss.setSession(ioc.getSession());
                    ss.setCourseName(ioc.getInstructionalOffering().getCourseName());
                    ioc.addToschedulingSubparts(ss);
                    if (parentSubpart != null) {
                        ss.setParentSubpart(parentSubpart);
                        parentSubpart.addTochildSubparts(ss);
                    }
                    changed = true;
                    isAdd = true;
                    addNote("\tdid not find existing matching scheduling subpart, created new one: "
                            + ss.getItypeDesc());
                }
                if (ss.isAutoSpreadInTime() == null) {
                    ss.setAutoSpreadInTime(ApplicationProperty.SchedulingSubpartAutoSpreadInTimeDefault.isTrue());
                }

                if (ss.isStudentAllowOverlap() == null) {
                    ss.setStudentAllowOverlap(ApplicationProperty.SchedulingSubpartStudentOverlapsDefault.isTrue());
                }

                if (ss.getMinutesPerWk() == null || !ss.getMinutesPerWk().equals(minPerWeek)) {
                    ss.setMinutesPerWk(minPerWeek);
                    addNote("\tsubpart minutes per week changed");
                    changed = true;
                }

                if (parentSubpart != null && ss.getParentSubpart() == null) {
                    ss.setParentSubpart(parentSubpart);
                    parentSubpart.addTochildSubparts(ss);
                    addNote("\tsubpart now has parent");
                    changed = true;
                } else if (parentSubpart != null
                        && !ss.getParentSubpart().getUniqueId().equals(parentSubpart.getUniqueId())) {
                    ss.getParentSubpart().getChildSubparts().remove(ss);
                    ss.setParentSubpart(parentSubpart);
                    parentSubpart.addTochildSubparts(ss);
                    addNote("\tsubpart has different parent");
                    changed = true;
                } else if (parentSubpart == null && ss.getParentSubpart() != null) {
                    ss.getParentSubpart().getChildSubparts().remove(ss);
                    ss.setParentSubpart(null);
                    addNote("\tsubpart no longer has parent");
                    changed = true;
                }

                if (elementSubpartCredit(subpart, ss)) {
                    addNote("\tsubpart credit changed");
                    changed = true;
                }

                if (changed) {
                    this.getHibSession().saveOrUpdate(ss);
                }

                if (elementSubpart(subpart, ioc, ss, allExistingSubparts)) {
                    changed = true;
                }
                if (changed) {
                    ChangeLog.addChange(getHibSession(), getManager(), session, ss,
                            ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                            (isAdd ? ChangeLog.Operation.CREATE : ChangeLog.Operation.UPDATE),
                            ioc.getControllingCourseOffering().getSubjectArea(), ioc.getDepartment());
                }
            }
        }
        if (!thisLevelSubparts.isEmpty()) {
            addNote("\tnot all subparts at this level had matches, deleted them");
            for (Iterator<?> it = thisLevelSubparts.values().iterator(); it.hasNext();) {
                SchedulingSubpart ss = (SchedulingSubpart) it.next();
                allExistingSubparts.remove(ss);
                deleteSchedulingSubpart(ss);
            }
            if (parentSubpart != null) {
                this.getHibSession().saveOrUpdate(parentSubpart);
                ChangeLog.addChange(getHibSession(), getManager(), session, parentSubpart,
                        ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.UPDATE,
                        ioc.getControllingCourseOffering().getSubjectArea(), ioc.getDepartment());
            }
            changed = true;
        }
        if (parentSubpart == null && !allExistingSubparts.isEmpty()) {
            addNote("\tnot all existing subparts had matches, deleted them");
            for (Iterator<?> it = allExistingSubparts.iterator(); it.hasNext();) {
                SchedulingSubpart ss = (SchedulingSubpart) it.next();
                if (ss.getParentSubpart() != null) {
                    ss.getParentSubpart().getChildSubparts().remove(ss);
                }
                deleteSchedulingSubpart(ss);
            }
            changed = true;
        }
        return (changed);
    }

    private void deleteDistributionPref(DistributionPref dp) {
        addNote("\tdeleting meets with distribution preference:  " + dp.preferenceText(true, false, "", ", ", ""));
        HashSet relatedInstructionalOfferings = new HashSet();
        Department dept = (Department) dp.getOwner();
        dept.getPreferences().remove(dp);
        for (Iterator i = dp.getDistributionObjects().iterator(); i.hasNext();) {
            DistributionObject dObj = (DistributionObject) i.next();
            PreferenceGroup pg = dObj.getPrefGroup();
            relatedInstructionalOfferings
                    .add((pg instanceof Class_ ? ((Class_) pg).getSchedulingSubpart() : (SchedulingSubpart) pg)
                            .getInstrOfferingConfig().getInstructionalOffering());
            pg.getDistributionObjects().remove(dObj);
            getHibSession().saveOrUpdate(pg);
        }
        ChangeLog.addChange(getHibSession(), getManager(), session, dp, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                ChangeLog.Operation.DELETE, null, dept);
        getHibSession().delete(dp);
        getHibSession().saveOrUpdate(dept);

        //        for (Iterator i=relatedInstructionalOfferings.iterator();i.hasNext();) {
        //            InstructionalOffering io = (InstructionalOffering)i.next();
        //            ChangeLog.addChange(getHibSession(), getManager(), session, io, ChangeLog.Source.DATA_IMPORT_OFFERINGS, ChangeLog.Operation.DELETE, io.getControllingCourseOffering().getSubjectArea(), null);
        //        }
    }

    private void deleteCourseOffering(CourseOffering co) {
        //TODO: may need to add a hook for deleting custom data from the database, could also just add an 
        // abstract cleanup method that is run at the end and let it take care of any customized things
        // that might need to be removed after their corresponding course offering has been deleted
        InstructionalOffering io = co.getInstructionalOffering();
        if (io.getCourseOfferings().size() == 1) {
            deleteInstructionalOffering(io);
        } else if (io.getCourseOfferings().size() > 1) {
            io.getCourseOfferings().remove(co);
            if (co.isIsControl().booleanValue()) {
                CourseOffering newControl = (CourseOffering) io.getCourseOfferings().iterator().next();
                newControl.setIsControl(new Boolean(true));
            }
            co.setInstructionalOffering(null);
            existingCourseOfferings.remove(co.getUniqueId());
            this.getHibSession().delete(co);
        } else {
            existingCourseOfferings.remove(co.getUniqueId());
            this.getHibSession().delete(co);
        }
    }

    private void deleteInstructionalOffering(InstructionalOffering io) {
        // remove the instructionalOffering from the list of existing instructional offerings
        existingInstructionalOfferings.remove(io.getUniqueId());

        // remove all course offering uniqueIds from the existing course offerings list 
        for (Iterator<?> it = io.getCourseOfferings().iterator(); it.hasNext();) {
            CourseOffering co = (CourseOffering) it.next();
            existingCourseOfferings.remove(co.getUniqueId());
        }

        // remove all class uniqueIds from the existing classes list and get rid of any dependent objects so the
        //    class can be deleted
        if (io.getInstrOfferingConfigs() != null) {
            for (Iterator<?> iocIt = io.getInstrOfferingConfigs().iterator(); iocIt.hasNext();) {
                InstrOfferingConfig ioc = (InstrOfferingConfig) iocIt.next();
                if (ioc.getSchedulingSubparts() != null) {
                    for (Iterator<?> ssIt = ioc.getSchedulingSubparts().iterator(); ssIt.hasNext();) {
                        SchedulingSubpart ss = (SchedulingSubpart) ssIt.next();
                        if (ss.getClasses() != null) {
                            for (Iterator<?> cIt = ss.getClasses().iterator(); cIt.hasNext();) {
                                Class_ c = (Class_) cIt.next();
                                existingClasses.remove(c.getUniqueId());
                                c.deleteAllDependentObjects(this.getHibSession(), false);
                            }
                        }
                    }
                }
            }
        }

        ChangeLog.addChange(getHibSession(), getManager(), session, io, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                ChangeLog.Operation.DELETE,
                (io.getControllingCourseOffering() == null ? null
                        : io.getControllingCourseOffering().getSubjectArea()),
                (io.getControllingCourseOffering() == null ? null
                        : io.getControllingCourseOffering().getDepartment()));
        this.getHibSession().delete(io);
        flush(true);
    }

    private void deleteInstrOffrConfig(InstrOfferingConfig ioc) {

        // remove all class uniqueIds from the existing classes list and get rid of any dependent objects so the
        //    class can be deleted
        if (ioc.getSchedulingSubparts() != null) {
            for (Iterator<?> ssIt = ioc.getSchedulingSubparts().iterator(); ssIt.hasNext();) {
                SchedulingSubpart ss = (SchedulingSubpart) ssIt.next();
                if (ss.getClasses() != null) {
                    for (Iterator<?> cIt = ss.getClasses().iterator(); cIt.hasNext();) {
                        Class_ c = (Class_) cIt.next();
                        existingClasses.remove(c.getUniqueId());
                        c.deleteAllDependentObjects(this.getHibSession(), false);
                    }
                }
            }
        }
        ioc.getInstructionalOffering().getInstrOfferingConfigs().remove(ioc);
        ChangeLog.addChange(getHibSession(), getManager(), session, ioc, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                ChangeLog.Operation.DELETE, ioc.getControllingCourseOffering().getSubjectArea(),
                ioc.getControllingCourseOffering().getDepartment());
        this.getHibSession().delete(ioc);
    }

    private void deleteSchedulingSubpart(SchedulingSubpart ss) {

        if (ss.getChildSubparts() != null) {
            for (Iterator cssIt = ss.getChildSubparts().iterator(); cssIt.hasNext();) {
                SchedulingSubpart css = (SchedulingSubpart) cssIt.next();
                deleteSchedulingSubpart(css);
            }
        }
        // remove all class uniqueIds from the existing classes list and get rid of any dependent objects so the
        //    class can be deleted
        if (ss.getClasses() != null) {
            for (Iterator<?> cIt = ss.getClasses().iterator(); cIt.hasNext();) {
                Class_ c = (Class_) cIt.next();
                existingClasses.remove(c.getUniqueId());
                c.deleteAllDependentObjects(this.getHibSession(), false);

            }
        }

        InstrOfferingConfig ioc = ss.getInstrOfferingConfig();

        SchedulingSubpart parentSubpart = ss.getParentSubpart();
        if (parentSubpart != null) {
            parentSubpart.getChildSubparts().remove(ss);
            ss.setParentSubpart(null);
        }
        ioc.getSchedulingSubparts().remove(ss);
        this.getHibSession().update(ioc);
        ChangeLog.addChange(getHibSession(), getManager(), session, ss, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                ChangeLog.Operation.DELETE, ioc.getControllingCourseOffering().getSubjectArea(),
                ioc.getControllingCourseOffering().getDepartment());
        //      this.getHibSession().delete(ss);
    }

    private void deleteClassInstructor(ClassInstructor ci) {
        ci.getInstructor().getClasses().remove(ci);
        ci.getClassInstructing().getClassInstructors().remove(ci);
        Class_ clazz = ci.getClassInstructing();
        ci.setClassInstructing(null);
        ci.setInstructor(null);
        ChangeLog.addChange(getHibSession(), getManager(), session, ci, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                ChangeLog.Operation.DELETE,
                clazz.getSchedulingSubpart().getControllingCourseOffering().getSubjectArea(),
                clazz.getSchedulingSubpart().getControllingCourseOffering().getDepartment());
        this.getHibSession().delete(ci);
    }

    private void deleteClass(Class_ c) {
        c.deleteAllDependentObjects(this.getHibSession(), false);
        c.getSchedulingSubpart().getClasses().remove(c);
        SchedulingSubpart ss = c.getSchedulingSubpart();
        ss.getClasses().remove(c);
        ChangeLog.addChange(getHibSession(), getManager(), session, c, ChangeLog.Source.DATA_IMPORT_OFFERINGS,
                ChangeLog.Operation.DELETE, ss.getControllingCourseOffering().getSubjectArea(),
                ss.getControllingCourseOffering().getDepartment());
        existingClasses.remove(c.getUniqueId());
        this.getHibSession().delete(c);
    }

    private CourseOffering findExistingCourseOffering(CourseOffering courseOffering, Long sessionId) {
        CourseOffering existingCo = null;
        if (courseOffering.getExternalUniqueId() != null) {
            existingCo = findCrsOffrForExternalId(courseOffering.getExternalUniqueId(), sessionId);
        }
        if (existingCo == null) {
            if (courseNumbersMustBeUnique) {
                existingCo = findCrsOffrForSubjCrs(courseOffering.getSubjectArea().getSubjectAreaAbbreviation(),
                        courseOffering.getCourseNbr(), sessionId);
            } else {
                existingCo = findCrsOffrForSubjCrsTitle(
                        courseOffering.getSubjectArea().getSubjectAreaAbbreviation(), courseOffering.getCourseNbr(),
                        courseOffering.getTitle(), sessionId);
            }
        }
        return (existingCo);
    }

    private CourseOffering findCrsOffrForExternalId(String externalId, Long sessionId) {
        return (CourseOffering) this.getHibSession().createQuery(
                "select distinct co from CourseOffering as co where co.externalUniqueId=:externalId and co.subjectArea.session.uniqueId=:sessionId")
                .setLong("sessionId", sessionId.longValue()).setString("externalId", externalId).setCacheable(true)
                .uniqueResult();
    }

    private CourseOffering findCrsOffrForSubjCrsTitle(String subjectAbbv, String crsNbr, String title,
            Long sessionId) {
        return (CourseOffering) this.getHibSession().createQuery(
                "select distinct co from CourseOffering as co where co.subjectArea.session.uniqueId=:sessionId and co.subjectArea.subjectAreaAbbreviation=:subjectAbbv and co.courseNbr=:courseNbr and co.title=:title")
                .setLong("sessionId", sessionId.longValue()).setString("subjectAbbv", subjectAbbv)
                .setString("courseNbr", crsNbr).setString("title", title).setCacheable(true).uniqueResult();
    }

    private CourseOffering findCrsOffrForSubjCrs(String subjectAbbv, String crsNbr, Long sessionId) {
        return (CourseOffering) this.getHibSession().createQuery(
                "select distinct co from CourseOffering as co where co.subjectArea.session.uniqueId=:sessionId and co.subjectArea.subjectAreaAbbreviation=:subjectAbbv and co.courseNbr=:courseNbr")
                .setLong("sessionId", sessionId.longValue()).setString("subjectAbbv", subjectAbbv)
                .setString("courseNbr", crsNbr).setCacheable(true).uniqueResult();
    }

    private InstructionalOffering findInstrOffrForExternalId(String externalId, Long sessionId) {
        return (InstructionalOffering) this.getHibSession().createQuery(
                "select distinct io from InstructionalOffering as io where io.externalUniqueId=:externalId and io.session.uniqueId=:sessionId")
                .setLong("sessionId", sessionId.longValue()).setString("externalId", externalId).setCacheable(true)
                .setFlushMode(FlushMode.MANUAL).uniqueResult();
    }

    private InstructionalOffering findInstrOffrForUniqueId(Long uniqueId) {
        return (InstructionalOffering) this.getHibSession()
                .createQuery("select distinct io from InstructionalOffering as io where io.uniqueId=:uniqueId")
                .setLong("uniqueId", uniqueId.longValue()).setCacheable(true).uniqueResult();
    }

    private CourseOffering findCourseOffrForUniqueId(Long uniqueId) {
        return (CourseOffering) this.getHibSession()
                .createQuery("select distinct co from CourseOffering as co where co.uniqueId=:uniqueId")
                .setLong("uniqueId", uniqueId.longValue()).setCacheable(true).uniqueResult();
    }

    private Class_ findClassForUniqueId(Long uniqueId) {
        return (Class_) this.getHibSession()
                .createQuery("select distinct c from Class_ as c where c.uniqueId=:uniqueId")
                .setLong("uniqueId", uniqueId.longValue()).setCacheable(true).uniqueResult();
    }

    private Class_ findClassForExternalUniqueId(String externalUniqueId) {
        return (Class_) this.getHibSession().createQuery(
                "select distinct c from Class_ as c where c.externalUniqueId=:externalUniqueId and c.schedulingSubpart.instrOfferingConfig.instructionalOffering.session.uniqueId=:sessionId")
                .setString("externalUniqueId", externalUniqueId)
                .setLong("sessionId", session.getUniqueId().longValue()).setCacheable(true).uniqueResult();
    }

    private Room findRoom(String id, String building, String roomNbr) {
        Room room = null;
        if (id != null) {
            room = (Room) this.getHibSession().createQuery(
                    "select distinct r from Room as r where r.externalUniqueId=:externalId and r.building.session.uniqueId=:sessionId")
                    .setLong("sessionId", session.getUniqueId().longValue()).setString("externalId", id)
                    .setCacheable(true).uniqueResult();
        }
        if (room == null) {
            room = (Room) this.getHibSession().createQuery(
                    "select distinct r from Room as r where r.roomNumber=:roomNbr and r.building.abbreviation = :building and r.session.uniqueId=:sessionId")
                    .setLong("sessionId", session.getUniqueId().longValue()).setString("building", building)
                    .setString("roomNbr", roomNbr).setCacheable(true).uniqueResult();
            if (id != null && room != null && room.getExternalUniqueId() != null
                    && !room.getExternalUniqueId().equals(id)) {
                room = null;
            }
        }
        return (room);
    }

    private NonUniversityLocation findNonUniversityLocation(String id, String name, Class_ c) {
        NonUniversityLocation location = null;
        List<?> possibleLocations = findNonUniversityLocationsWithIdOrName(id, name);
        if (possibleLocations != null) {
            for (Iterator<?> lIt = possibleLocations.iterator(); lIt.hasNext();) {
                NonUniversityLocation l = (NonUniversityLocation) lIt.next();
                if (l.getRoomDepts() != null) {
                    for (Iterator<?> rdIt = l.getRoomDepts().iterator(); rdIt.hasNext();) {
                        RoomDept rd = (RoomDept) rdIt.next();
                        if (rd.getDepartment().getUniqueId()
                                .equals(c.getSchedulingSubpart().getControllingDept().getUniqueId())) {
                            location = l;
                            break;
                        }
                    }
                }
                if (location != null) {
                    break;
                }
            }
        }
        return (location);
    }

    private void loadSubjectAreas(Long sessionId) {
        List<?> subjects = new ArrayList<Object>();
        subjects = this.getHibSession()
                .createQuery("select distinct sa from SubjectArea as sa where sa.session.uniqueId=:sessionId")
                .setLong("sessionId", sessionId.longValue()).setCacheable(true).list();
        for (Iterator<?> it = subjects.iterator(); it.hasNext();) {
            SubjectArea sa = (SubjectArea) it.next();
            subjectAreas.put(sa.getSubjectAreaAbbreviation(), sa);
        }
    }

    private void loadItypes() {
        Set<?> itypeDescs = ItypeDesc.findAll(false);
        for (Iterator<?> it = itypeDescs.iterator(); it.hasNext();) {
            ItypeDesc itype = (ItypeDesc) it.next();
            itypes.put(itype.getAbbv().trim(), itype);
            itypesBySisRef.put(itype.getSis_ref(), itype);
        }
    }

    private ItypeDesc findItypeForString(String itypeRef) {
        ItypeDesc itype = itypes.get(itypeRef);
        if (itype == null) {
            itype = itypesBySisRef.get(itypeRef);
        }
        return (itype);
    }

    private void loadExistingInstructionalOfferings(Long sessionId) throws Exception {

        for (Iterator<?> it = InstructionalOffering.findAll(sessionId).iterator(); it.hasNext();) {
            InstructionalOffering io = (InstructionalOffering) it.next();
            existingInstructionalOfferings.add(io.getUniqueId());
        }
    }

    private void loadExistingCourseOfferings(Long sessionId) throws Exception {

        for (Iterator<?> it = CourseOffering.findAll(sessionId).iterator(); it.hasNext();) {
            CourseOffering courseOffering = (CourseOffering) it.next();
            existingCourseOfferings.add(courseOffering.getUniqueId());
        }
    }

    private void loadMeetsWithDistributionType() {
        meetsWithType = (DistributionType) this.getHibSession()
                .createQuery("from DistributionType dt where dt.reference = 'MEET_WITH'").uniqueResult();
    }

    private void loadCanShareRoomDistributionType() {
        canShareRoomType = (DistributionType) this.getHibSession()
                .createQuery("from DistributionType dt where dt.reference = 'CAN_SHARE_ROOM'").uniqueResult();
    }

    private void loadRequiredPrefLevel() {
        requiredPrefLevel = (PreferenceLevel) this.getHibSession()
                .createQuery("from PreferenceLevel pl where pl.prefName = 'Required'").uniqueResult();
    }

    private void loadExistingClasses(Long sessionId) throws Exception {
        for (Iterator<?> it = Class_.findAll(sessionId).iterator(); it.hasNext();) {
            Class_ c = (Class_) it.next();
            if (c.getExternalUniqueId() != null) {
                existingClasses.add(c.getUniqueId());
            } else {
                existingClasses.add(c.getUniqueId());
            }
        }
    }

    @Override
    protected String getEmailSubject() {
        return ("Course Offering Import Results - " + session.getAcademicYearTerm());
    }

    protected class ImportCourseOffering {

        /**
         * 
         */
        private CourseOffering courseOffering;
        private Element element;

        public ImportCourseOffering(CourseOffering courseOffering, Element element) {
            this.courseOffering = courseOffering;
            this.element = element;
        }

        public CourseOffering getCourseOffering() {
            return courseOffering;
        }

        public void setCourseOffering(CourseOffering courseOffering) {
            this.courseOffering = courseOffering;
        }

        public Element getElement() {
            return element;
        }

        public void setElement(Element element) {
            this.element = element;
        }

    }

}