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