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