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