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