    001package org.cpsolver.studentsct;
    002
    003import java.io.File;
    004import java.util.ArrayList;
    005import java.util.BitSet;
    006import java.util.Collections;
    007import java.util.Comparator;
    008import java.util.HashSet;
    009import java.util.HashMap;
    010import java.util.Iterator;
    011import java.util.List;
    012import java.util.Map;
    013import java.util.Set;
    014
    015import org.cpsolver.coursett.model.Placement;
    016import org.cpsolver.coursett.model.RoomLocation;
    017import org.cpsolver.coursett.model.TimeLocation;
    018import org.cpsolver.ifs.assignment.Assignment;
    019import org.cpsolver.ifs.model.Constraint;
    020import org.cpsolver.ifs.util.DistanceMetric;
    021import org.cpsolver.ifs.util.Progress;
    022import org.cpsolver.studentsct.constraint.FixedAssignments;
    023import org.cpsolver.studentsct.filter.StudentFilter;
    024import org.cpsolver.studentsct.model.AreaClassificationMajor;
    025import org.cpsolver.studentsct.model.Choice;
    026import org.cpsolver.studentsct.model.Config;
    027import org.cpsolver.studentsct.model.Course;
    028import org.cpsolver.studentsct.model.CourseRequest;
    029import org.cpsolver.studentsct.model.Enrollment;
    030import org.cpsolver.studentsct.model.FreeTimeRequest;
    031import org.cpsolver.studentsct.model.Instructor;
    032import org.cpsolver.studentsct.model.Offering;
    033import org.cpsolver.studentsct.model.Request;
    034import org.cpsolver.studentsct.model.Request.RequestPriority;
    035import org.cpsolver.studentsct.model.Student.BackToBackPreference;
    036import org.cpsolver.studentsct.model.Student.ModalityPreference;
    037import org.cpsolver.studentsct.model.Student.StudentPriority;
    038import org.cpsolver.studentsct.model.RequestGroup;
    039import org.cpsolver.studentsct.model.Section;
    040import org.cpsolver.studentsct.model.Student;
    041import org.cpsolver.studentsct.model.StudentGroup;
    042import org.cpsolver.studentsct.model.Subpart;
    043import org.cpsolver.studentsct.model.Unavailability;
    044import org.cpsolver.studentsct.reservation.CourseReservation;
    045import org.cpsolver.studentsct.reservation.CourseRestriction;
    046import org.cpsolver.studentsct.reservation.CurriculumOverride;
    047import org.cpsolver.studentsct.reservation.CurriculumReservation;
    048import org.cpsolver.studentsct.reservation.CurriculumRestriction;
    049import org.cpsolver.studentsct.reservation.DummyReservation;
    050import org.cpsolver.studentsct.reservation.GroupReservation;
    051import org.cpsolver.studentsct.reservation.IndividualReservation;
    052import org.cpsolver.studentsct.reservation.IndividualRestriction;
    053import org.cpsolver.studentsct.reservation.LearningCommunityReservation;
    054import org.cpsolver.studentsct.reservation.Reservation;
    055import org.cpsolver.studentsct.reservation.ReservationOverride;
    056import org.cpsolver.studentsct.reservation.Restriction;
    057import org.cpsolver.studentsct.reservation.UniversalOverride;
    058import org.dom4j.Document;
    059import org.dom4j.DocumentException;
    060import org.dom4j.Element;
    061import org.dom4j.io.SAXReader;
    062
    063/**
    064 * Load student sectioning model from an XML file.
    065 *
    066 * <br>
    067 * <br>
    068 * Parameters:
    069 * <table border='1'><caption>Related Solver Parameters</caption>
    070 * <tr>
    071 * <th>Parameter</th>
    072 * <th>Type</th>
    073 * <th>Comment</th>
    074 * </tr>
    075 * <tr>
    076 * <td>General.Input</td>
    077 * <td>{@link String}</td>
    078 * <td>Path of an XML file to be loaded</td>
    079 * </tr>
    080 * <tr>
    081 * <td>Xml.LoadBest</td>
    082 * <td>{@link Boolean}</td>
    083 * <td>If true, load best assignments</td>
    084 * </tr>
    085 * <tr>
    086 * <td>Xml.LoadInitial</td>
    087 * <td>{@link Boolean}</td>
    088 * <td>If false, load initial assignments</td>
    089 * </tr>
    090 * <tr>
    091 * <td>Xml.LoadCurrent</td>
    092 * <td>{@link Boolean}</td>
    093 * <td>If true, load current assignments</td>
    094 * </tr>
    095 * <tr>
    096 * <td>Xml.LoadOfferings</td>
    097 * <td>{@link Boolean}</td>
    098 * <td>If true, load offerings (and their stucture, i.e., courses,
    099 * configurations, subparts and sections)</td>
    100 * </tr>
    101 * <tr>
    102 * <td>Xml.LoadStudents</td>
    103 * <td>{@link Boolean}</td>
    104 * <td>If true, load students (and their requests)</td>
    105 * </tr>
    106 * <tr>
    107 * <td>Xml.StudentFilter</td>
    108 * <td>{@link StudentFilter}</td>
    109 * <td>If provided, students are filtered by the given student filter</td>
    110 * </tr>
    111 * </table>
    112 *
    113 * <br>
    114 * <br>
    115 * Usage:
    116 * <pre><code>
    117 * StudentSectioningModel model = new StudentSectioningModel(cfg);<br>
    118 * new StudentSectioningXMLLoader(model).load();<br>
    119 * </code></pre>
    120 *
    121 * @author  Tomáš Müller
    122 * @version StudentSct 1.3 (Student Sectioning)<br>
    123 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
    124 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    125 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    126 * <br>
    127 *          This library is free software; you can redistribute it and/or modify
    128 *          it under the terms of the GNU Lesser General Public License as
    129 *          published by the Free Software Foundation; either version 3 of the
    130 *          License, or (at your option) any later version. <br>
    131 * <br>
    132 *          This library is distributed in the hope that it will be useful, but
    133 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    134 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    135 *          Lesser General Public License for more details. <br>
    136 * <br>
    137 *          You should have received a copy of the GNU Lesser General Public
    138 *          License along with this library; if not see
    139 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    140 */
    141
    142public class StudentSectioningXMLLoader extends StudentSectioningLoader {
    143    private static org.apache.logging.log4j.Logger sLogger = org.apache.logging.log4j.LogManager.getLogger(StudentSectioningXMLLoader.class);
    144
    145    private File iInputFile;
    146    private File iTimetableFile = null;
    147    private boolean iLoadBest = false;
    148    private boolean iLoadInitial = false;
    149    private boolean iLoadCurrent = false;
    150    private boolean iLoadOfferings = true;
    151    private boolean iLoadStudents = true;
    152    private StudentFilter iStudentFilter = null;
    153    private boolean iWaitlistCritical = false;
    154    private boolean iMoveCriticalUp = false;
    155
    156    /**
    157     * Constructor
    158     *
    159     * @param model
    160     *            student sectioning model
    161     * @param assignment current assignment
    162     */
    163    public StudentSectioningXMLLoader(StudentSectioningModel model, Assignment<Request, Enrollment> assignment) {
    164        super(model, assignment);
    165        iInputFile = new File(getModel().getProperties().getProperty("General.Input",
    166                "." + File.separator + "solution.xml"));
    167        if (getModel().getProperties().getProperty("General.InputTimetable") != null)
    168            iTimetableFile = new File(getModel().getProperties().getProperty("General.InputTimetable"));
    169        iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true);
    170        iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true);
    171        iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true);
    172        iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true);
    173        iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true);
    174        iWaitlistCritical = getModel().getProperties().getPropertyBoolean("Xml.WaitlistCritical", false);
    175        iMoveCriticalUp = getModel().getProperties().getPropertyBoolean("Xml.MoveCriticalUp", false);
    176        if (getModel().getProperties().getProperty("Xml.StudentFilter") != null) {
    177            try {
    178                iStudentFilter = (StudentFilter) Class.forName(
    179                        getModel().getProperties().getProperty("Xml.StudentFilter")).getConstructor(new Class[] {})
    180                        .newInstance(new Object[] {});
    181            } catch (Exception e) {
    182                sLogger.error("Unable to create student filter, reason: " + e.getMessage(), e);
    183            }
    184        }
    185    }
    186
    187    /** Set input file (e.g., if it is not set by General.Input property)
    188     * @param inputFile input file
    189     **/
    190    public void setInputFile(File inputFile) {
    191        iInputFile = inputFile;
    192    }
    193
    194    /** Set student filter
    195     * @param filter student filter
    196     **/
    197    public void setStudentFilter(StudentFilter filter) {
    198        iStudentFilter = filter;
    199    }
    200
    201    /** Set whether to load students
    202     * @param loadStudents true if students are to be loaded
    203     **/
    204    public void setLoadStudents(boolean loadStudents) {
    205        iLoadStudents = loadStudents;
    206    }
    207
    208    /** Set whether to load offerings
    209     * @param loadOfferings true if instructional offerings are to be loaded
    210     **/
    211    public void setLoadOfferings(boolean loadOfferings) {
    212        iLoadOfferings = loadOfferings;
    213    }
    214
    215    /** Create BitSet from a bit string */
    216    private static BitSet createBitSet(String bitString) {
    217        BitSet ret = new BitSet(bitString.length());
    218        for (int i = 0; i < bitString.length(); i++)
    219            if (bitString.charAt(i) == '1')
    220                ret.set(i);
    221        return ret;
    222    }
    223
    224    /** Load the file */
    225    @Override
    226    public void load() throws Exception {
    227        sLogger.debug("Reading XML data from " + iInputFile);
    228
    229        Document document = (new SAXReader()).read(iInputFile);
    230        Element root = document.getRootElement();
    231
    232        load(root);
    233    }
    234
    235    public void load(Document document) {
    236        Element root = document.getRootElement();
    237
    238        if (getModel() != null && root.element("travel-times") != null)
    239            loadTravelTimes(root.element("travel-times"), getModel().getDistanceMetric());
    240
    241        Progress.getInstance(getModel()).load(root, true);
    242        Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ...");
    243
    244        Map<Long, Offering> offeringTable = new HashMap<Long, Offering>();
    245        Map<Long, Course> courseTable = new HashMap<Long, Course>();
    246
    247        if (root.element("offerings") != null) {
    248            loadOfferings(root.element("offerings"), offeringTable, courseTable, null);
    249        }
    250
    251        List<Enrollment> bestEnrollments = new ArrayList<Enrollment>();
    252        List<Enrollment> currentEnrollments = new ArrayList<Enrollment>();
    253        if (root.element("students") != null) {
    254            loadStudents(root.element("students"), offeringTable, courseTable, bestEnrollments, currentEnrollments);
    255        }
    256
    257        if (root.element("constraints") != null)
    258            loadLinkedSections(root.element("constraints"), offeringTable);
    259
    260        if (!bestEnrollments.isEmpty()) assignBest(bestEnrollments);
    261        if (!currentEnrollments.isEmpty()) assignCurrent(currentEnrollments);
    262
    263        if (iMoveCriticalUp) moveCriticalRequestsUp();
    264
    265        boolean hasFixed = false;
    266        for (Request r: getModel().variables()) {
    267            if (r instanceof CourseRequest && ((CourseRequest)r).isFixed()) {
    268                hasFixed = true; break;
    269            }
    270        }
    271        if (hasFixed)
    272            getModel().addGlobalConstraint(new FixedAssignments());
    273    }
    274
    275    /**
    276     * Load data from the given XML root
    277     * @param root document root
    278     * @throws DocumentException when XML cannot be read or parsed
    279     */
    280    protected void load(Element root) throws DocumentException {
    281        sLogger.debug("Root element: " + root.getName());
    282        if (!"sectioning".equals(root.getName())) {
    283            sLogger.error("Given XML file is not student sectioning problem.");
    284            return;
    285        }
    286
    287        if (iLoadOfferings && getModel().getDistanceConflict() != null && root.element("travel-times") != null)
    288            loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric());
    289
    290        Map<Long, Placement> timetable = null;
    291        if (iTimetableFile != null) {
    292            sLogger.info("Reading timetable from " + iTimetableFile + " ...");
    293            Document timetableDocument = (new SAXReader()).read(iTimetableFile);
    294            Element timetableRoot = timetableDocument.getRootElement();
    295            if (!"timetable".equals(timetableRoot.getName())) {
    296                sLogger.error("Given XML file is not course timetabling problem.");
    297                return;
    298            }
    299            timetable = loadTimetable(timetableRoot);
    300        }
    301
    302        Progress.getInstance(getModel()).load(root, true);
    303        Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ...");
    304
    305        if (root.attributeValue("term") != null)
    306            getModel().getProperties().setProperty("Data.Term", root.attributeValue("term"));
    307        if (root.attributeValue("year") != null)
    308            getModel().getProperties().setProperty("Data.Year", root.attributeValue("year"));
    309        if (root.attributeValue("initiative") != null)
    310            getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative"));
    311
    312        Map<Long, Offering> offeringTable = new HashMap<Long, Offering>();
    313        Map<Long, Course> courseTable = new HashMap<Long, Course>();
    314
    315        if (iLoadOfferings && root.element("offerings") != null) {
    316            loadOfferings(root.element("offerings"), offeringTable, courseTable, timetable);
    317        } else {
    318            for (Offering offering : getModel().getOfferings()) {
    319                offeringTable.put(Long.valueOf(offering.getId()), offering);
    320                for (Course course : offering.getCourses()) {
    321                    courseTable.put(Long.valueOf(course.getId()), course);
    322                }
    323            }
    324        }
    325
    326        List<Enrollment> bestEnrollments = new ArrayList<Enrollment>();
    327        List<Enrollment> currentEnrollments = new ArrayList<Enrollment>();
    328        if (iLoadStudents && root.element("students") != null) {
    329            loadStudents(root.element("students"), offeringTable, courseTable, bestEnrollments, currentEnrollments);
    330        }
    331
    332        if (iLoadOfferings && root.element("constraints") != null)
    333            loadLinkedSections(root.element("constraints"), offeringTable);
    334
    335        if (!bestEnrollments.isEmpty()) assignBest(bestEnrollments);
    336        if (!currentEnrollments.isEmpty()) assignCurrent(currentEnrollments);
    337
    338        if (iMoveCriticalUp) moveCriticalRequestsUp();
    339
    340        sLogger.debug("Model successfully loaded.");
    341    }
    342
    343    /**
    344     * Load offerings
    345     * @param offeringsEl offerings element
    346     * @param offeringTable offering table
    347     * @param courseTable course table
    348     * @param timetable provided timetable (null if to be loaded from the given document)
    349     */
    350    protected void loadOfferings(Element offeringsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, Map<Long, Placement> timetable) {
    351        HashMap<Long, Config> configTable = new HashMap<Long, Config>();
    352        HashMap<Long, Subpart> subpartTable = new HashMap<Long, Subpart>();
    353        HashMap<Long, Section> sectionTable = new HashMap<Long, Section>();
    354        for (Iterator<?> i = offeringsEl.elementIterator("offering"); i.hasNext();) {
    355            Element offeringEl = (Element) i.next();
    356            Offering offering = new Offering(
    357                    Long.parseLong(offeringEl.attributeValue("id")),
    358                    offeringEl.attributeValue("name", "O" + offeringEl.attributeValue("id")));
    359            offering.setDummy("true".equals(offeringEl.attributeValue("dummy", "false")));
    360            offeringTable.put(Long.valueOf(offering.getId()), offering);
    361            getModel().addOffering(offering);
    362
    363            for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) {
    364                Element courseEl = (Element) j.next();
    365                Course course = loadCourse(courseEl, offering);
    366                courseTable.put(Long.valueOf(course.getId()), course);
    367            }
    368
    369            for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) {
    370                Element configEl = (Element) j.next();
    371                Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable);
    372                configTable.put(config.getId(), config);
    373            }
    374
    375            for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) {
    376                Element reservationEl = (Element)j.next();
    377                loadReservation(reservationEl, offering, configTable, sectionTable);
    378            }
    379
    380            for (Iterator<?> j = offeringEl.elementIterator("restriction"); j.hasNext(); ) {
    381                Element restrictionEl = (Element)j.next();
    382                loadRestriction(restrictionEl, offering, configTable, sectionTable);
    383            }
    384        }
    385    }
    386
    387    /**
    388     * Load course
    389     * @param courseEl course element
    390     * @param offering parent offering
    391     * @return loaded course
    392     */
    393    protected Course loadCourse(Element courseEl, Offering offering) {
    394        Course course = new Course(
    395                Long.parseLong(courseEl.attributeValue("id")),
    396                courseEl.attributeValue("subjectArea", ""),
    397                courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")),
    398                offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")),
    399                Integer.parseInt(courseEl.attributeValue("projected", "0")));
    400        course.setCredit(courseEl.attributeValue("credit"));
    401        String credits = courseEl.attributeValue("credits");
    402        if (credits != null)
    403            course.setCreditValue(Float.valueOf(credits));
    404        course.setNote(courseEl.attributeValue("note"));
    405        course.setType(courseEl.attributeValue("type"));
    406        course.setTitle(courseEl.attributeValue("title"));
    407        return course;
    408    }
    409
    410    /**
    411     * Load config
    412     * @param configEl config element
    413     * @param offering parent offering
    414     * @param subpartTable subpart table (of the offering)
    415     * @param sectionTable section table (of the offering)
    416     * @param timetable provided timetable
    417     * @return loaded config
    418     */
    419    protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
    420        Config config = new Config
    421                (Long.parseLong(configEl.attributeValue("id")),
    422                Integer.parseInt(configEl.attributeValue("limit", "-1")),
    423                configEl.attributeValue("name", "G" + configEl.attributeValue("id")),
    424                offering);
    425        Element imEl = configEl.element("instructional-method");
    426        if (imEl != null) {
    427            config.setInstructionalMethodId(Long.parseLong(imEl.attributeValue("id")));
    428            config.setInstructionalMethodName(imEl.attributeValue("name", "M" + imEl.attributeValue("id")));
    429            config.setInstructionalMethodReference(imEl.attributeValue("reference", config.getInstructionalMethodName()));
    430        }
    431        for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) {
    432            Element subpartEl = (Element) k.next();
    433            Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable);
    434            subpartTable.put(Long.valueOf(subpart.getId()), subpart);
    435        }
    436        return config;
    437    }
    438
    439    /**
    440     * Load subpart
    441     * @param subpartEl supart element
    442     * @param config parent config
    443     * @param subpartTable subpart table (of the offering)
    444     * @param sectionTable section table (of the offering)
    445     * @param timetable provided timetable
    446     * @return loaded subpart
    447     */
    448    protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
    449        Subpart parentSubpart = null;
    450        if (subpartEl.attributeValue("parent") != null)
    451            parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent")));
    452        Subpart subpart = new Subpart(
    453                Long.parseLong(subpartEl.attributeValue("id")),
    454                subpartEl.attributeValue("itype"),
    455                subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")),
    456                config,
    457                parentSubpart);
    458        subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false")));
    459        subpart.setCredit(subpartEl.attributeValue("credit"));
    460        String credits = subpartEl.attributeValue("credits");
    461        if (credits != null)
    462            subpart.setCreditValue(Float.valueOf(credits));
    463
    464        for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) {
    465            Element sectionEl = (Element) l.next();
    466            Section section = loadSection(sectionEl, subpart, sectionTable, timetable);
    467            sectionTable.put(Long.valueOf(section.getId()), section);
    468        }
    469
    470        return subpart;
    471    }
    472
    473    /**
    474     * Load section
    475     * @param sectionEl section element
    476     * @param subpart parent subpart
    477     * @param sectionTable section table (of the offering)
    478     * @param timetable provided timetable
    479     * @return loaded section
    480     */
    481    @SuppressWarnings("deprecation")
    482    protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
    483        Section parentSection = null;
    484        if (sectionEl.attributeValue("parent") != null)
    485            parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent")));
    486        Placement placement = null;
    487        if (timetable != null) {
    488            placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id")));
    489        } else {
    490            TimeLocation time = null;
    491            Element timeEl = sectionEl.element("time");
    492            if (timeEl != null) {
    493                time = new TimeLocation(
    494                        Integer.parseInt(timeEl.attributeValue("days"), 2),
    495                        Integer.parseInt(timeEl.attributeValue("start")),
    496                        Integer.parseInt(timeEl.attributeValue("length")), 0, 0,
    497                        timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")),
    498                        timeEl.attributeValue("datePatternName", ""),
    499                        createBitSet(timeEl.attributeValue("dates")),
    500                        Integer.parseInt(timeEl.attributeValue("breakTime", "0")));
    501                if (timeEl.attributeValue("pattern") != null)
    502                    time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
    503            }
    504            List<RoomLocation> rooms = new ArrayList<RoomLocation>();
    505            for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) {
    506                Element roomEl = (Element) m.next();
    507                Double posX = null, posY = null;
    508                if (roomEl.attributeValue("location") != null) {
    509                    String loc = roomEl.attributeValue("location");
    510                    posX = Double.valueOf(loc.substring(0, loc.indexOf(',')));
    511                    posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1));
    512                }
    513                RoomLocation room = new RoomLocation(
    514                        Long.valueOf(roomEl.attributeValue("id")),
    515                        roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")),
    516                        roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")),
    517                        0, Integer.parseInt(roomEl.attributeValue("capacity")),
    518                        posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null);
    519                rooms.add(room);
    520            }
    521            placement = (time == null ? null : new Placement(null, time, rooms));
    522        }
    523
    524        List<Instructor> instructors = new ArrayList<Instructor>();
    525        for (Iterator<?> m = sectionEl.elementIterator("instructor"); m.hasNext(); ) {
    526            Element instructorEl = (Element)m.next();
    527            instructors.add(new Instructor(Long.parseLong(instructorEl.attributeValue("id")), instructorEl.attributeValue("externalId"), instructorEl.attributeValue("name"), instructorEl.attributeValue("email")));
    528        }
    529        if (instructors.isEmpty() && sectionEl.attributeValue("instructorIds") != null)
    530            instructors = Instructor.toInstructors(sectionEl.attributeValue("instructorIds"), sectionEl.attributeValue("instructorNames"));
    531        Section section = new Section(
    532                Long.parseLong(sectionEl.attributeValue("id")),
    533                Integer.parseInt(sectionEl.attributeValue("limit")),
    534                sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")),
    535                subpart, placement, instructors, parentSection);
    536
    537        section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0")));
    538        section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0")));
    539        section.setCancelled("true".equalsIgnoreCase(sectionEl.attributeValue("cancelled", "false")));
    540        section.setEnabled("true".equalsIgnoreCase(sectionEl.attributeValue("enabled", "true")));
    541        section.setOnline("true".equalsIgnoreCase(sectionEl.attributeValue("online", "false")));
    542        section.setPast("true".equalsIgnoreCase(sectionEl.attributeValue("past", "false")));
    543        for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) {
    544            Element cNameEl = (Element)m.next();
    545            section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText());
    546        }
    547        Element ignoreEl = sectionEl.element("no-conflicts");
    548        if (ignoreEl != null) {
    549            for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); )
    550                section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id")));
    551        }
    552
    553        return section;
    554    }
    555
    556    /**
    557     * Load reservation
    558     * @param reservationEl reservation element
    559     * @param offering parent offering
    560     * @param configTable config table (of the offering)
    561     * @param sectionTable section table (of the offering)
    562     * @return loaded reservation
    563     */
    564    protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) {
    565        Reservation r = null;
    566        if ("individual".equals(reservationEl.attributeValue("type"))) {
    567            Set<Long> studentIds = new HashSet<Long>();
    568            for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) {
    569                Element studentEl = (Element)k.next();
    570                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
    571            }
    572            r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds);
    573        } else if ("group".equals(reservationEl.attributeValue("type"))) {
    574            Set<Long> studentIds = new HashSet<Long>();
    575            for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) {
    576                Element studentEl = (Element)k.next();
    577                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
    578            }
    579            r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")),
    580                    Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
    581                    offering, studentIds);
    582        } else if ("lc".equals(reservationEl.attributeValue("type"))) {
    583            Set<Long> studentIds = new HashSet<Long>();
    584            for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) {
    585                Element studentEl = (Element)k.next();
    586                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
    587            }
    588            long courseId = Long.parseLong(reservationEl.attributeValue("course"));
    589            for (Course course: offering.getCourses()) {
    590                if (course.getId() == courseId)
    591                    r = new LearningCommunityReservation(Long.valueOf(reservationEl.attributeValue("id")),
    592                            Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
    593                            course, studentIds);
    594            }
    595        } else if ("curriculum".equals(reservationEl.attributeValue("type")) || "curriculum-override".equals(reservationEl.attributeValue("type"))) {
    596            List<String> acadAreas = new ArrayList<String>();
    597            for (Iterator<?> k = reservationEl.elementIterator("area"); k.hasNext(); ) {
    598                Element areaEl = (Element)k.next();
    599                acadAreas.add(areaEl.attributeValue("code"));
    600            }
    601            if (acadAreas.isEmpty() && reservationEl.attributeValue("area") != null)
    602                acadAreas.add(reservationEl.attributeValue("area"));
    603            List<String> classifications = new ArrayList<String>();
    604            for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) {
    605                Element clasfEl = (Element)k.next();
    606                classifications.add(clasfEl.attributeValue("code"));
    607            }
    608            List<String> majors = new ArrayList<String>();
    609            for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) {
    610                Element majorEl = (Element)k.next();
    611                majors.add(majorEl.attributeValue("code"));
    612            }
    613            List<String> minors = new ArrayList<String>();
    614            for (Iterator<?> k = reservationEl.elementIterator("minor"); k.hasNext(); ) {
    615                Element minorEl = (Element)k.next();
    616                minors.add(minorEl.attributeValue("code"));
    617            }
    618            if ("curriculum".equals(reservationEl.attributeValue("type")))
    619                r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")),
    620                        Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
    621                        offering,
    622                        acadAreas, classifications, majors, minors);
    623            else
    624                r = new CurriculumOverride(Long.valueOf(reservationEl.attributeValue("id")),
    625                        Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
    626                        offering,
    627                        acadAreas, classifications, majors, minors);
    628            for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) {
    629                Element majorEl = (Element)k.next();
    630                for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) {
    631                    Element concentrationEl = (Element)l.next();
    632                    ((CurriculumReservation)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code"));
    633                }
    634            }
    635        } else if ("course".equals(reservationEl.attributeValue("type"))) {
    636            long courseId = Long.parseLong(reservationEl.attributeValue("course"));
    637            for (Course course: offering.getCourses()) {
    638                if (course.getId() == courseId)
    639                    r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course);
    640            }
    641        } else if ("dummy".equals(reservationEl.attributeValue("type"))) {
    642            r = new DummyReservation(offering);
    643        } else if ("universal".equals(reservationEl.attributeValue("type"))) {
    644            r = new UniversalOverride(
    645                    Long.valueOf(reservationEl.attributeValue("id")),
    646                    "true".equals(reservationEl.attributeValue("override", "false")),
    647                    Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
    648                    offering,
    649                    reservationEl.attributeValue("filter"));
    650        } else if ("override".equals(reservationEl.attributeValue("type"))) {
    651            Set<Long> studentIds = new HashSet<Long>();
    652            for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) {
    653                Element studentEl = (Element)k.next();
    654                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
    655            }
    656            r = new ReservationOverride(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds);
    657        }
    658        if (r == null) {
    659            sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type"));
    660            return null;
    661        }
    662        r.setExpired("true".equals(reservationEl.attributeValue("expired", "false")));
    663        for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) {
    664            Element configEl = (Element)k.next();
    665            r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id"))));
    666        }
    667        for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) {
    668            Element sectionEl = (Element)k.next();
    669            r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))), false);
    670        }
    671        r.setPriority(Integer.parseInt(reservationEl.attributeValue("priority", String.valueOf(r.getPriority()))));
    672        r.setMustBeUsed("true".equals(reservationEl.attributeValue("mustBeUsed", r.mustBeUsed() ? "true" : "false")));
    673        r.setAllowOverlap("true".equals(reservationEl.attributeValue("allowOverlap", r.isAllowOverlap() ? "true" : "false")));
    674        r.setCanAssignOverLimit("true".equals(reservationEl.attributeValue("canAssignOverLimit", r.canAssignOverLimit() ? "true" : "false")));
    675        r.setAllowDisabled("true".equals(reservationEl.attributeValue("allowDisabled", r.isAllowDisabled() ? "true" : "false")));
    676        r.setNeverIncluded("true".equals(reservationEl.attributeValue("neverIncluded", "false")));
    677        r.setBreakLinkedSections("true".equals(reservationEl.attributeValue("breakLinkedSections", "false")));
    678        return r;
    679    }
    680
    681    /**
    682     * Load restriction
    683     * @param restrictionEl restriction element
    684     * @param offering parent offering
    685     * @param configTable config table (of the offering)
    686     * @param sectionTable section table (of the offering)
    687     * @return loaded restriction
    688     */
    689    protected Restriction loadRestriction(Element restrictionEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) {
    690        Restriction r = null;
    691        if ("individual".equals(restrictionEl.attributeValue("type"))) {
    692            Set<Long> studentIds = new HashSet<Long>();
    693            for (Iterator<?> k = restrictionEl.elementIterator("student"); k.hasNext(); ) {
    694                Element studentEl = (Element)k.next();
    695                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
    696            }
    697            r = new IndividualRestriction(Long.valueOf(restrictionEl.attributeValue("id")), offering, studentIds);
    698        } else if ("curriculum".equals(restrictionEl.attributeValue("type"))) {
    699            List<String> acadAreas = new ArrayList<String>();
    700            for (Iterator<?> k = restrictionEl.elementIterator("area"); k.hasNext(); ) {
    701                Element areaEl = (Element)k.next();
    702                acadAreas.add(areaEl.attributeValue("code"));
    703            }
    704            if (acadAreas.isEmpty() && restrictionEl.attributeValue("area") != null)
    705                acadAreas.add(restrictionEl.attributeValue("area"));
    706            List<String> classifications = new ArrayList<String>();
    707            for (Iterator<?> k = restrictionEl.elementIterator("classification"); k.hasNext(); ) {
    708                Element clasfEl = (Element)k.next();
    709                classifications.add(clasfEl.attributeValue("code"));
    710            }
    711            List<String> majors = new ArrayList<String>();
    712            for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) {
    713                Element majorEl = (Element)k.next();
    714                majors.add(majorEl.attributeValue("code"));
    715            }
    716            List<String> minors = new ArrayList<String>();
    717            for (Iterator<?> k = restrictionEl.elementIterator("minor"); k.hasNext(); ) {
    718                Element minorEl = (Element)k.next();
    719                minors.add(minorEl.attributeValue("code"));
    720            }
    721            r = new CurriculumRestriction(Long.valueOf(restrictionEl.attributeValue("id")),
    722                    offering,
    723                    acadAreas, classifications, majors, minors);
    724            for (Iterator<?> k = restrictionEl.elementIterator("major"); k.hasNext(); ) {
    725                Element majorEl = (Element)k.next();
    726                for (Iterator<?> l = majorEl.elementIterator("concentration"); l.hasNext(); ) {
    727                    Element concentrationEl = (Element)l.next();
    728                    ((CurriculumRestriction)r).addConcentration(majorEl.attributeValue("code"), concentrationEl.attributeValue("code"));
    729                }
    730            }
    731        } else if ("course".equals(restrictionEl.attributeValue("type"))) {
    732            long courseId = Long.parseLong(restrictionEl.attributeValue("course"));
    733            for (Course course: offering.getCourses()) {
    734                if (course.getId() == courseId)
    735                    r = new CourseRestriction(Long.valueOf(restrictionEl.attributeValue("id")), course);
    736            }
    737        }
    738        if (r == null) {
    739            sLogger.error("Unknown reservation type "+ restrictionEl.attributeValue("type"));
    740            return null;
    741        }
    742        for (Iterator<?> k = restrictionEl.elementIterator("config"); k.hasNext(); ) {
    743            Element configEl = (Element)k.next();
    744            r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id"))));
    745        }
    746        for (Iterator<?> k = restrictionEl.elementIterator("section"); k.hasNext(); ) {
    747            Element sectionEl = (Element)k.next();
    748            r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))));
    749        }
    750        return r;
    751    }
    752
    753    /**
    754     * Load given timetable
    755     * @param timetableRoot document root in the course timetabling XML format
    756     * @return loaded timetable (map class id: assigned placement)
    757     */
    758    protected Map<Long, Placement> loadTimetable(Element timetableRoot) {
    759        Map<Long, Placement> timetable = new HashMap<Long, Placement>();
    760        HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>();
    761        for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) {
    762            Element roomEl = (Element)i.next();
    763            Long roomId = Long.valueOf(roomEl.attributeValue("id"));
    764            Double posX = null, posY = null;
    765            if (roomEl.attributeValue("location") != null) {
    766                String loc = roomEl.attributeValue("location");
    767                posX = Double.valueOf(loc.substring(0, loc.indexOf(',')));
    768                posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1));
    769            }
    770            RoomLocation room = new RoomLocation(
    771                    Long.valueOf(roomEl.attributeValue("id")),
    772                    roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")),
    773                    roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")),
    774                    0, Integer.parseInt(roomEl.attributeValue("capacity")),
    775                    posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null);
    776            rooms.put(roomId, room);
    777        }
    778        for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) {
    779            Element classEl = (Element)i.next();
    780            Long classId = Long.valueOf(classEl.attributeValue("id"));
    781            TimeLocation time = null;
    782            Element timeEl = null;
    783            for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) {
    784                Element e = (Element)j.next();
    785                if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; }
    786            }
    787            if (timeEl != null) {
    788                time = new TimeLocation(
    789                        Integer.parseInt(timeEl.attributeValue("days"), 2),
    790                        Integer.parseInt(timeEl.attributeValue("start")),
    791                        Integer.parseInt(timeEl.attributeValue("length")), 0, 0,
    792                        classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")),
    793                        classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")),
    794                        Integer.parseInt(timeEl.attributeValue("breakTime", "0")));
    795                if (timeEl.attributeValue("pattern") != null)
    796                    time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
    797            }
    798            List<RoomLocation> room = new ArrayList<RoomLocation>();
    799            for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) {
    800                Element roomEl = (Element) j.next();
    801                if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue;
    802                room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id"))));
    803            }
    804            Placement placement = (time == null ? null : new Placement(null, time, room));
    805            if (placement != null)
    806                timetable.put(classId, placement);
    807        }
    808        return timetable;
    809    }
    810
    811    /**
    812     * Load travel times
    813     * @param travelTimesEl travel-time element
    814     * @param metric distance metric to be populated
    815     */
    816    protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) {
    817        for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) {
    818            Element travelTimeEl = (Element)i.next();
    819            metric.addTravelTime(
    820                    Long.valueOf(travelTimeEl.attributeValue("id1")),
    821                    Long.valueOf(travelTimeEl.attributeValue("id2")),
    822                    Integer.valueOf(travelTimeEl.attributeValue("minutes")));
    823        }
    824    }
    825
    826    /**
    827     * Load linked sections
    828     * @param constraintsEl constraints element
    829     * @param offeringTable offering table
    830     */
    831    protected void loadLinkedSections(Element constraintsEl, Map<Long, Offering> offeringTable) {
    832        for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) {
    833            Element linkedEl = (Element) i.next();
    834            List<Section> sections = new ArrayList<Section>();
    835            for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) {
    836                Element sectionEl = (Element) j.next();
    837                Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering")));
    838                sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id"))));
    839            }
    840            getModel().addLinkedSections("true".equals(linkedEl.attributeValue("mustBeUsed", "false")), sections);
    841        }
    842    }
    843
    844    /**
    845     * Load students
    846     * @param studentsEl students element
    847     * @param offeringTable offering table
    848     * @param courseTable course table
    849     */
    850    protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, List<Enrollment> bestEnrollments, List<Enrollment> currentEnrollments) {
    851        for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) {
    852            Element studentEl = (Element) i.next();
    853            Student student = loadStudent(studentEl, offeringTable);
    854            if (iStudentFilter != null && !iStudentFilter.accept(student))
    855                continue;
    856            for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) {
    857                Element requestEl = (Element) j.next();
    858                Request request = loadRequest(requestEl, student, offeringTable, courseTable);
    859                if (request == null) continue;
    860
    861                Element initialEl = requestEl.element("initial");
    862                if (iLoadInitial && initialEl != null) {
    863                    Enrollment enrollment = loadEnrollment(initialEl, request);
    864                    if (enrollment != null)
    865                        request.setInitialAssignment(enrollment);
    866                }
    867                Element currentEl = requestEl.element("current");
    868                if (iLoadCurrent && currentEl != null) {
    869                    Enrollment enrollment = loadEnrollment(currentEl, request);
    870                    if (enrollment != null)
    871                        currentEnrollments.add(enrollment);
    872                }
    873                Element bestEl = requestEl.element("best");
    874                if (iLoadBest && bestEl != null) {
    875                    Enrollment enrollment = loadEnrollment(bestEl, request);
    876                    if (enrollment != null)
    877                        bestEnrollments.add(enrollment);
    878                }
    879                Element fixedEl = requestEl.element("fixed");
    880                if (fixedEl != null && request instanceof CourseRequest)
    881                    ((CourseRequest)request).setFixedValue(loadEnrollment(fixedEl, request));
    882            }
    883            getModel().addStudent(student);
    884        }
    885    }
    886
    887    /**
    888     * Save best enrollments
    889     * @param bestEnrollments best enrollments
    890     */
    891    protected void assignBest(List<Enrollment> bestEnrollments) {
    892        // Enrollments with a reservation must go first, enrollments with an override go last
    893        for (Enrollment enrollment : bestEnrollments) {
    894            if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue;
    895            if (!enrollment.getStudent().isAvailable(enrollment)) {
    896                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " is conflicting: student not available.");
    897                continue;
    898            }
    899            Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment);
    900            if (conflicts.isEmpty())
    901                getAssignment().assign(0, enrollment);
    902            else
    903                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " conflicts with " + conflicts);
    904        }
    905        for (Enrollment enrollment : bestEnrollments) {
    906            if (enrollment.getReservation() != null) continue;
    907            if (!enrollment.getStudent().isAvailable(enrollment)) {
    908                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " is conflicting: student not available.");
    909                continue;
    910            }
    911            Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment);
    912            if (conflicts.isEmpty())
    913                getAssignment().assign(0, enrollment);
    914            else
    915                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " conflicts with " + conflicts);
    916        }
    917        for (Enrollment enrollment : bestEnrollments) {
    918            if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue;
    919            if (!enrollment.getStudent().isAvailable(enrollment)) {
    920                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " is conflicting: student not available.");
    921                continue;
    922            }
    923            Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment);
    924            if (conflicts.isEmpty())
    925                getAssignment().assign(0, enrollment);
    926            else
    927                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " conflicts with " + conflicts);
    928        }
    929        getModel().saveBest(getAssignment());
    930    }
    931
    932    /**
    933     * Assign current enrollments
    934     * @param currentEnrollments current enrollments
    935     */
    936    protected void assignCurrent(List<Enrollment> currentEnrollments) {
    937        for (Request request : getModel().variables())
    938            getAssignment().unassign(0, request);
    939        // Enrollments with a reservation must go first, enrollments with an override go last
    940        for (Enrollment enrollment : currentEnrollments) {
    941            if (enrollment.getReservation() == null || enrollment.getReservation().isExpired()) continue;
    942            if (!enrollment.getStudent().isAvailable(enrollment)) {
    943                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " is conflicting: student not available.");
    944                continue;
    945            }
    946            Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment);
    947            if (conflicts.isEmpty())
    948                getAssignment().assign(0, enrollment);
    949            else
    950                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " conflicts with " + conflicts);
    951        }
    952        for (Enrollment enrollment : currentEnrollments) {
    953            if (enrollment.getReservation() != null) continue;
    954            if (!enrollment.getStudent().isAvailable(enrollment)) {
    955                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " is conflicting: student not available.");
    956                continue;
    957            }
    958            Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment);
    959            if (conflicts.isEmpty())
    960                getAssignment().assign(0, enrollment);
    961            else
    962                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " conflicts with " + conflicts);
    963        }
    964        for (Enrollment enrollment : currentEnrollments) {
    965            if (enrollment.getReservation() == null || !enrollment.getReservation().isExpired()) continue;
    966            if (!enrollment.getStudent().isAvailable(enrollment)) {
    967                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " is conflicting: student not available.");
    968                continue;
    969            }
    970            Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(getAssignment(), enrollment);
    971            if (conflicts.isEmpty())
    972                getAssignment().assign(0, enrollment);
    973            else
    974                sLogger.warn("[" + enrollment.getStudent().getExternalId() + "] Enrollment " + enrollment + " conflicts with " + conflicts);
    975        }
    976    }
    977
    978    /**
    979     * Load student
    980     * @param studentEl student element
    981     * @param offeringTable offering table
    982     * @return loaded student
    983     */
    984    protected Student loadStudent(Element studentEl, Map<Long, Offering> offeringTable) {
    985        Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy")));
    986        if (studentEl.attributeValue("priority") != null)
    987            student.setPriority(StudentPriority.getPriority(studentEl.attributeValue("priority")));
    988        if ("true".equals(studentEl.attributeValue("shortDistances")))
    989            student.setNeedShortDistances(true);
    990        if ("true".equals(studentEl.attributeValue("allowDisabled")))
    991            student.setAllowDisabled(true);
    992        student.setExternalId(studentEl.attributeValue("externalId"));
    993        student.setName(studentEl.attributeValue("name"));
    994        student.setStatus(studentEl.attributeValue("status"));
    995        String minCredit = studentEl.attributeValue("minCredit");
    996        if (minCredit != null)
    997            student.setMinCredit(Float.parseFloat(minCredit));
    998        String maxCredit = studentEl.attributeValue("maxCredit");
    999        if (maxCredit != null)
    1000            student.setMaxCredit(Float.parseFloat(maxCredit));
    1001        String classFirstDate = studentEl.attributeValue("classFirstDate");
    1002        if (classFirstDate != null)
    1003            student.setClassFirstDate(Integer.parseInt(classFirstDate));
    1004        String classLastDate = studentEl.attributeValue("classLastDate");
    1005        if (classLastDate != null)
    1006            student.setClassLastDate(Integer.parseInt(classLastDate));
    1007        String modality = studentEl.attributeValue("modality");
    1008        if (modality != null)
    1009            student.setModalityPreference(ModalityPreference.valueOf(modality));
    1010        String btb = studentEl.attributeValue("btb");
    1011        if (btb != null)
    1012            student.setBackToBackPreference(BackToBackPreference.valueOf(btb));
    1013
    1014        List<String[]> clasf = new ArrayList<String[]>();
    1015        List<String[]> major = new ArrayList<String[]>();
    1016        for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) {
    1017            Element requestEl = (Element) j.next();
    1018            if ("classification".equals(requestEl.getName())) {
    1019                clasf.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")});
    1020            } else if ("major".equals(requestEl.getName())) {
    1021                major.add(new String[] {requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")});
    1022            } else if ("minor".equals(requestEl.getName())) {
    1023                if ("A".equals(requestEl.attributeValue("area")))
    1024                    student.getAccommodations().add(requestEl.attributeValue("code"));
    1025                else
    1026                    student.getGroups().add(new StudentGroup(requestEl.attributeValue("area"), requestEl.attributeValue("code"), requestEl.attributeValue("label")));
    1027            } else if ("unavailability".equals(requestEl.getName())) {
    1028                Offering offering = offeringTable.get(Long.parseLong(requestEl.attributeValue("offering")));
    1029                Section section = (offering == null ? null : offering.getSection(Long.parseLong(requestEl.attributeValue("section"))));
    1030                if (section != null) {
    1031                    Unavailability ua = new Unavailability(student, section, "true".equals(requestEl.attributeValue("allowOverlap")));
    1032                    ua.setTeachingAssignment("true".equals(requestEl.attributeValue("ta", "false")));
    1033                    if (requestEl.attributeValue("course") != null)
    1034                        ua.setCourseId(Long.valueOf(requestEl.attributeValue("course")));
    1035                }
    1036            } else if ("acm".equals(requestEl.getName())) {
    1037                if (requestEl.attributeValue("minor") != null)
    1038                    student.getAreaClassificationMinors().add(new AreaClassificationMajor(
    1039                            requestEl.attributeValue("area"), requestEl.attributeValue("areaName"),
    1040                            requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"),
    1041                            requestEl.attributeValue("minor"), requestEl.attributeValue("minorName"),
    1042                            requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"),
    1043                            requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"),
    1044                            requestEl.attributeValue("program"), requestEl.attributeValue("programName"),
    1045                            requestEl.attributeValue("campus"), requestEl.attributeValue("campusName"),
    1046                            requestEl.attributeValue("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight"))));
    1047                else
    1048                    student.getAreaClassificationMajors().add(new AreaClassificationMajor(
    1049                            requestEl.attributeValue("area"), requestEl.attributeValue("areaName"),
    1050                            requestEl.attributeValue("classification"), requestEl.attributeValue("classificationName"),
    1051                            requestEl.attributeValue("major"), requestEl.attributeValue("majorName"),
    1052                            requestEl.attributeValue("concentration"), requestEl.attributeValue("concentrationName"),
    1053                            requestEl.attributeValue("degree"), requestEl.attributeValue("degreeName"),
    1054                            requestEl.attributeValue("program"), requestEl.attributeValue("programName"),
    1055                            requestEl.attributeValue("campus"), requestEl.attributeValue("campusName"),
    1056                            requestEl.attributeValue("weight") == null ? null : Double.valueOf(requestEl.attributeValue("weight"))));
    1057            } else if ("group".equals(requestEl.getName())) {
    1058                student.getGroups().add(new StudentGroup(requestEl.attributeValue("type"), requestEl.attributeValue("reference"), requestEl.attributeValue("name")));
    1059            } else if ("accommodation".equals(requestEl.getName())) {
    1060                student.getAccommodations().add(requestEl.attributeValue("reference"));
    1061            } else if ("advisor".equals(requestEl.getName())) {
    1062                student.getAdvisors().add(new Instructor(0l, requestEl.attributeValue("externalId"), requestEl.attributeValue("name"), requestEl.attributeValue("email")));
    1063            }
    1064        }
    1065        for (int i = 0; i < Math.min(clasf.size(), major.size()); i++) {
    1066            student.getAreaClassificationMajors().add(new AreaClassificationMajor(clasf.get(i)[0],clasf.get(i)[1],major.get(i)[1]));
    1067        }
    1068        return student;
    1069    }
    1070
    1071    /**
    1072     * Load request
    1073     * @param requestEl request element
    1074     * @param student parent student
    1075     * @param offeringTable offering table
    1076     * @param courseTable course table
    1077     * @return loaded request
    1078     */
    1079    protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) {
    1080        if ("freeTime".equals(requestEl.getName())) {
    1081            return loadFreeTime(requestEl, student);
    1082        } else if ("course".equals(requestEl.getName())) {
    1083            return loadCourseRequest(requestEl, student, offeringTable, courseTable);
    1084        } else {
    1085            return null;
    1086        }
    1087    }
    1088
    1089    /**
    1090     * Load free time request
    1091     * @param requestEl request element
    1092     * @param student parent student
    1093     * @return loaded free time request
    1094     */
    1095    public FreeTimeRequest loadFreeTime(Element requestEl, Student student) {
    1096        TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2),
    1097                Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl
    1098                        .attributeValue("length")), 0, 0,
    1099                requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl
    1100                        .attributeValue("datePattern")), "", createBitSet(requestEl
    1101                        .attributeValue("dates")), 0);
    1102        FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")),
    1103                Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl
    1104                        .attributeValue("alternative")), student, time);
    1105        if (requestEl.attributeValue("weight") != null)
    1106            request.setWeight(Double.parseDouble(requestEl.attributeValue("weight")));
    1107        return request;
    1108    }
    1109
    1110    /**
    1111     * Load course request
    1112     * @param requestEl request element
    1113     * @param student parent student
    1114     * @param offeringTable offering table
    1115     * @param courseTable course table
    1116     * @return loaded course request
    1117     */
    1118    public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) {
    1119        List<Course> courses = new ArrayList<Course>();
    1120        courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course"))));
    1121        for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();)
    1122            courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course"))));
    1123        Long timeStamp = null;
    1124        if (requestEl.attributeValue("timeStamp") != null)
    1125            timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp"));
    1126        CourseRequest courseRequest = new CourseRequest(
    1127                Long.parseLong(requestEl.attributeValue("id")),
    1128                Integer.parseInt(requestEl.attributeValue("priority")),
    1129                "true".equals(requestEl.attributeValue("alternative")),
    1130                student, courses,
    1131                "true".equals(requestEl.attributeValue("waitlist", "false")),
    1132                RequestPriority.valueOf(requestEl.attributeValue("importance",
    1133                        "true".equals(requestEl.attributeValue("critical", "false")) ? RequestPriority.Critical.name() : RequestPriority.Normal.name())),
    1134                timeStamp);
    1135        if (iWaitlistCritical && RequestPriority.Critical.isCritical(courseRequest) && !courseRequest.isAlternative()) courseRequest.setWaitlist(true);
    1136        if (requestEl.attributeValue("weight") != null)
    1137            courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight")));
    1138        for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) {
    1139            Element choiceEl = (Element) k.next();
    1140            courseRequest.getWaitlistedChoices().add(
    1141                    new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText()));
    1142        }
    1143        for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) {
    1144            Element choiceEl = (Element) k.next();
    1145            courseRequest.getSelectedChoices().add(
    1146                    new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText()));
    1147        }
    1148        for (Iterator<?> k = requestEl.elementIterator("required"); k.hasNext();) {
    1149            Element choiceEl = (Element) k.next();
    1150            courseRequest.getRequiredChoices().add(
    1151                    new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText()));
    1152        }
    1153        groups: for (Iterator<?> k = requestEl.elementIterator("group"); k.hasNext();) {
    1154            Element groupEl = (Element) k.next();
    1155            long gid = Long.parseLong(groupEl.attributeValue("id"));
    1156            String gname = groupEl.attributeValue("name", "g" + gid);
    1157            Course course = courseTable.get(Long.valueOf(groupEl.attributeValue("course")));
    1158            for (RequestGroup g: course.getRequestGroups()) {
    1159                if (g.getId() == gid) {
    1160                    courseRequest.addRequestGroup(g);
    1161                    continue groups;
    1162                }
    1163            }
    1164            courseRequest.addRequestGroup(new RequestGroup(gid, gname, course));
    1165        }
    1166        return courseRequest;
    1167    }
    1168
    1169    /**
    1170     * Load enrollment
    1171     * @param enrollmentEl enrollment element (current, best, or initial)
    1172     * @param request parent request
    1173     * @return loaded enrollment
    1174     */
    1175    protected Enrollment loadEnrollment(Element enrollmentEl, Request request) {
    1176        if (request instanceof CourseRequest) {
    1177            CourseRequest courseRequest = (CourseRequest) request;
    1178            HashSet<Section> sections = new HashSet<Section>();
    1179            for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) {
    1180                Element sectionEl = (Element) k.next();
    1181                Section section = courseRequest.getSection(Long.parseLong(sectionEl
    1182                        .attributeValue("id")));
    1183                sections.add(section);
    1184            }
    1185            Reservation reservation = null;
    1186            if (enrollmentEl.attributeValue("reservation", null) != null) {
    1187                long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation"));
    1188                for (Course course: courseRequest.getCourses())
    1189                    for (Reservation r: course.getOffering().getReservations())
    1190                        if (r.getId() == reservationId) { reservation = r; break; }
    1191            }
    1192            if (!sections.isEmpty()) {
    1193                if (enrollmentEl.attributeValue("course") != null) {
    1194                    Course course = courseRequest.getCourse(Long.valueOf(enrollmentEl.attributeValue("course")));
    1195                    if (course != null)
    1196                        return courseRequest.createEnrollment(course, sections, reservation);
    1197                }
    1198                return courseRequest.createEnrollment(sections, reservation);
    1199            }
    1200        } else if (request instanceof FreeTimeRequest) {
    1201            return ((FreeTimeRequest)request).createEnrollment();
    1202        }
    1203        return null;
    1204    }
    1205
    1206    protected void moveCriticalRequestsUp() {
    1207        for (Student student: getModel().getStudents()) {
    1208            int assigned = 0, critical = 0;
    1209            for (Request r: student.getRequests()) {
    1210                if (r instanceof CourseRequest) {
    1211                    if (r.getInitialAssignment() != null) assigned ++;
    1212                    if (r.getRequestPriority() != RequestPriority.Normal) critical ++;
    1213                }
    1214            }
    1215            if ((getModel().getKeepInitialAssignments() && assigned > 0) || critical > 0) {
    1216                Collections.sort(student.getRequests(), new Comparator<Request>() {
    1217                    @Override
    1218                    public int compare(Request r1, Request r2) {
    1219                        if (r1.isAlternative() != r2.isAlternative()) return r1.isAlternative() ? 1 : -1;
    1220                        if (getModel().getKeepInitialAssignments()) {
    1221                            boolean a1 = (r1 instanceof CourseRequest && r1.getInitialAssignment() != null);
    1222                            boolean a2 = (r2 instanceof CourseRequest && r2.getInitialAssignment() != null);
    1223                            if (a1 != a2) return a1 ? -1 : 1;
    1224                        }
    1225                        int c1 = r1.getRequestPriority().ordinal();
    1226                        int c2 = r2.getRequestPriority().ordinal();
    1227                        if (c1 != c2) return c1 < c2 ? -1 : 1;
    1228                        return r1.getPriority() < r2.getPriority() ? -1 : 1;
    1229                    }
    1230                });
    1231                int p = 0;
    1232                for (Request r: student.getRequests())
    1233                    r.setPriority(p++);
    1234            }
    1235        }
    1236    }
    1237
    1238}