001package net.sf.cpsolver.studentsct;
002
003import java.io.File;
004import java.util.ArrayList;
005import java.util.BitSet;
006import java.util.HashSet;
007import java.util.HashMap;
008import java.util.Iterator;
009import java.util.List;
010import java.util.Map;
011import java.util.Set;
012
013import net.sf.cpsolver.coursett.model.Placement;
014import net.sf.cpsolver.coursett.model.RoomLocation;
015import net.sf.cpsolver.coursett.model.TimeLocation;
016import net.sf.cpsolver.ifs.model.Constraint;
017import net.sf.cpsolver.ifs.util.DistanceMetric;
018import net.sf.cpsolver.ifs.util.Progress;
019import net.sf.cpsolver.studentsct.filter.StudentFilter;
020import net.sf.cpsolver.studentsct.model.AcademicAreaCode;
021import net.sf.cpsolver.studentsct.model.Choice;
022import net.sf.cpsolver.studentsct.model.Config;
023import net.sf.cpsolver.studentsct.model.Course;
024import net.sf.cpsolver.studentsct.model.CourseRequest;
025import net.sf.cpsolver.studentsct.model.Enrollment;
026import net.sf.cpsolver.studentsct.model.FreeTimeRequest;
027import net.sf.cpsolver.studentsct.model.Offering;
028import net.sf.cpsolver.studentsct.model.Request;
029import net.sf.cpsolver.studentsct.model.Section;
030import net.sf.cpsolver.studentsct.model.Student;
031import net.sf.cpsolver.studentsct.model.Subpart;
032import net.sf.cpsolver.studentsct.reservation.CourseReservation;
033import net.sf.cpsolver.studentsct.reservation.CurriculumReservation;
034import net.sf.cpsolver.studentsct.reservation.DummyReservation;
035import net.sf.cpsolver.studentsct.reservation.GroupReservation;
036import net.sf.cpsolver.studentsct.reservation.IndividualReservation;
037import net.sf.cpsolver.studentsct.reservation.Reservation;
038
039import org.dom4j.Document;
040import org.dom4j.DocumentException;
041import org.dom4j.Element;
042import org.dom4j.io.SAXReader;
043
044/**
045 * Load student sectioning model from an XML file.
046 * 
047 * <br>
048 * <br>
049 * Parameters:
050 * <table border='1'>
051 * <tr>
052 * <th>Parameter</th>
053 * <th>Type</th>
054 * <th>Comment</th>
055 * </tr>
056 * <tr>
057 * <td>General.Input</td>
058 * <td>{@link String}</td>
059 * <td>Path of an XML file to be loaded</td>
060 * </tr>
061 * <tr>
062 * <td>Xml.LoadBest</td>
063 * <td>{@link Boolean}</td>
064 * <td>If true, load best assignments</td>
065 * </tr>
066 * <tr>
067 * <td>Xml.LoadInitial</td>
068 * <td>{@link Boolean}</td>
069 * <td>If false, load initial assignments</td>
070 * </tr>
071 * <tr>
072 * <td>Xml.LoadCurrent</td>
073 * <td>{@link Boolean}</td>
074 * <td>If true, load current assignments</td>
075 * </tr>
076 * <tr>
077 * <td>Xml.LoadOfferings</td>
078 * <td>{@link Boolean}</td>
079 * <td>If true, load offerings (and their stucture, i.e., courses,
080 * configurations, subparts and sections)</td>
081 * </tr>
082 * <tr>
083 * <td>Xml.LoadStudents</td>
084 * <td>{@link Boolean}</td>
085 * <td>If true, load students (and their requests)</td>
086 * </tr>
087 * <tr>
088 * <td>Xml.StudentFilter</td>
089 * <td>{@link StudentFilter}</td>
090 * <td>If provided, students are filtered by the given student filter</td>
091 * </tr>
092 * </table>
093 * 
094 * <br>
095 * <br>
096 * Usage:<br>
097 * <code>
098 * StudentSectioningModel model = new StudentSectioningModel(cfg);<br>
099 * new StudentSectioningXMLLoader(model).load();<br>
100 * </code>
101 * 
102 * @version StudentSct 1.2 (Student Sectioning)<br>
103 *          Copyright (C) 2007 - 2010 Tomáš Müller<br>
104 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
105 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
106 * <br>
107 *          This library is free software; you can redistribute it and/or modify
108 *          it under the terms of the GNU Lesser General Public License as
109 *          published by the Free Software Foundation; either version 3 of the
110 *          License, or (at your option) any later version. <br>
111 * <br>
112 *          This library is distributed in the hope that it will be useful, but
113 *          WITHOUT ANY WARRANTY; without even the implied warranty of
114 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
115 *          Lesser General Public License for more details. <br>
116 * <br>
117 *          You should have received a copy of the GNU Lesser General Public
118 *          License along with this library; if not see
119 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
120 */
121
122public class StudentSectioningXMLLoader extends StudentSectioningLoader {
123    private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger
124            .getLogger(StudentSectioningXMLLoader.class);
125
126    private File iInputFile;
127    private File iTimetableFile = null;
128    private boolean iLoadBest = false;
129    private boolean iLoadInitial = false;
130    private boolean iLoadCurrent = false;
131    private boolean iLoadOfferings = true;
132    private boolean iLoadStudents = true;
133    private StudentFilter iStudentFilter = null;
134
135    /**
136     * Constructor
137     * 
138     * @param model
139     *            student sectioning model
140     */
141    public StudentSectioningXMLLoader(StudentSectioningModel model) {
142        super(model);
143        iInputFile = new File(getModel().getProperties().getProperty("General.Input",
144                "." + File.separator + "solution.xml"));
145        if (getModel().getProperties().getProperty("General.InputTimetable") != null)
146            iTimetableFile = new File(getModel().getProperties().getProperty("General.InputTimetable"));
147        iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true);
148        iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true);
149        iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true);
150        iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true);
151        iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true);
152        if (getModel().getProperties().getProperty("Xml.StudentFilter") != null) {
153            try {
154                iStudentFilter = (StudentFilter) Class.forName(
155                        getModel().getProperties().getProperty("Xml.StudentFilter")).getConstructor(new Class[] {})
156                        .newInstance(new Object[] {});
157            } catch (Exception e) {
158                sLogger.error("Unable to create student filter, reason: " + e.getMessage(), e);
159            }
160        }
161    }
162
163    /** Set input file (e.g., if it is not set by General.Input property) */
164    public void setInputFile(File inputFile) {
165        iInputFile = inputFile;
166    }
167
168    /** Set student filter */
169    public void setStudentFilter(StudentFilter filter) {
170        iStudentFilter = filter;
171    }
172
173    /** Set whether to load students */
174    public void setLoadStudents(boolean loadStudents) {
175        iLoadStudents = loadStudents;
176    }
177
178    /** Set whether to load offerings */
179    public void setLoadOfferings(boolean loadOfferings) {
180        iLoadOfferings = loadOfferings;
181    }
182
183    /** Create BitSet from a bit string */
184    private static BitSet createBitSet(String bitString) {
185        BitSet ret = new BitSet(bitString.length());
186        for (int i = 0; i < bitString.length(); i++)
187            if (bitString.charAt(i) == '1')
188                ret.set(i);
189        return ret;
190    }
191
192    /** Load the file */
193    @Override
194    public void load() throws Exception {
195        sLogger.debug("Reading XML data from " + iInputFile);
196
197        Document document = (new SAXReader()).read(iInputFile);
198        Element root = document.getRootElement();
199
200        load(root);
201    }
202    
203    /**
204     * Load data from the given XML root
205     * @param root document root
206     * @throws DocumentException
207     */
208    protected void load(Element root) throws DocumentException {
209        sLogger.debug("Root element: " + root.getName());
210        if (!"sectioning".equals(root.getName())) {
211            sLogger.error("Given XML file is not student sectioning problem.");
212            return;
213        }
214        
215        if (iLoadOfferings && getModel().getDistanceConflict() != null && root.element("travel-times") != null)
216            loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric());
217        
218        Map<Long, Placement> timetable = null;
219        if (iTimetableFile != null) {
220            sLogger.info("Reading timetable from " + iTimetableFile + " ...");
221            Document timetableDocument = (new SAXReader()).read(iTimetableFile);
222            Element timetableRoot = timetableDocument.getRootElement();
223            if (!"timetable".equals(timetableRoot.getName())) {
224                sLogger.error("Given XML file is not course timetabling problem.");
225                return;
226            }
227            timetable = loadTimetable(timetableRoot);
228        }
229
230        Progress.getInstance(getModel()).load(root, true);
231        Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ...");
232
233        if (root.attributeValue("term") != null)
234            getModel().getProperties().setProperty("Data.Term", root.attributeValue("term"));
235        if (root.attributeValue("year") != null)
236            getModel().getProperties().setProperty("Data.Year", root.attributeValue("year"));
237        if (root.attributeValue("initiative") != null)
238            getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative"));
239
240        Map<Long, Offering> offeringTable = new HashMap<Long, Offering>();
241        Map<Long, Course> courseTable = new HashMap<Long, Course>();
242
243        if (iLoadOfferings && root.element("offerings") != null) {
244            loadOfferings(root.element("offerings"), offeringTable, courseTable, timetable);
245        } else {
246            for (Offering offering : getModel().getOfferings()) {
247                offeringTable.put(new Long(offering.getId()), offering);
248                for (Course course : offering.getCourses()) {
249                    courseTable.put(new Long(course.getId()), course);
250                }
251            }
252        }
253
254        if (iLoadStudents && root.element("students") != null) {
255            loadStudents(root.element("students"), offeringTable, courseTable);
256        }
257        
258        if (iLoadOfferings && root.element("constraints") != null) 
259            loadLinkedSectiond(root.element("constraints"), offeringTable);
260        
261        sLogger.debug("Model successfully loaded.");
262    }
263    
264    /**
265     * Load offerings
266     * @param offeringsEl offerings element
267     * @param offeringTable offering table
268     * @param courseTable course table
269     * @param timetable provided timetable (null if to be loaded from the given document)
270     */
271    protected void loadOfferings(Element offeringsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, Map<Long, Placement> timetable) {
272        HashMap<Long, Config> configTable = new HashMap<Long, Config>();
273        HashMap<Long, Subpart> subpartTable = new HashMap<Long, Subpart>();
274        HashMap<Long, Section> sectionTable = new HashMap<Long, Section>();
275        for (Iterator<?> i = offeringsEl.elementIterator("offering"); i.hasNext();) {
276            Element offeringEl = (Element) i.next();
277            Offering offering = new Offering(
278                    Long.parseLong(offeringEl.attributeValue("id")),
279                    offeringEl.attributeValue("name", "O" + offeringEl.attributeValue("id")));
280            offeringTable.put(new Long(offering.getId()), offering);
281            getModel().addOffering(offering);
282            
283            for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) {
284                Element courseEl = (Element) j.next();
285                Course course = loadCourse(courseEl, offering);
286                courseTable.put(new Long(course.getId()), course);
287            }
288            
289            for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) {
290                Element configEl = (Element) j.next();
291                Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable);
292                configTable.put(config.getId(), config);
293            }
294            
295            for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) {
296                Element reservationEl = (Element)j.next();
297                loadReservation(reservationEl, offering, configTable, sectionTable);
298            } 
299        }
300    }
301    
302    /**
303     * Load course
304     * @param courseEl course element
305     * @param offering parent offering
306     * @return loaded course
307     */
308    protected Course loadCourse(Element courseEl, Offering offering) {
309        Course course = new Course(
310                Long.parseLong(courseEl.attributeValue("id")),
311                courseEl.attributeValue("subjectArea", ""),
312                courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")),
313                offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")),
314                Integer.parseInt(courseEl.attributeValue("projected", "0")));
315        return course;
316    }
317    
318    /**
319     * Load config
320     * @param configEl config element
321     * @param offering parent offering
322     * @param subpartTable subpart table (of the offering)
323     * @param sectionTable section table (of the offering)
324     * @param timetable provided timetable
325     * @return loaded config
326     */
327    protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
328        Config config = new Config
329                (Long.parseLong(configEl.attributeValue("id")),
330                Integer.parseInt(configEl.attributeValue("limit", "-1")),
331                configEl.attributeValue("name", "G" + configEl.attributeValue("id")),
332                offering);
333        for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) {
334            Element subpartEl = (Element) k.next();
335            Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable);
336            subpartTable.put(new Long(subpart.getId()), subpart);
337        }
338        return config;
339    }
340    
341    /**
342     * Load subpart
343     * @param subpartEl supart element
344     * @param config parent config
345     * @param subpartTable subpart table (of the offering)
346     * @param sectionTable section table (of the offering)
347     * @param timetable provided timetable
348     * @return loaded subpart
349     */
350    protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
351        Subpart parentSubpart = null;
352        if (subpartEl.attributeValue("parent") != null)
353            parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent")));
354        Subpart subpart = new Subpart(
355                Long.parseLong(subpartEl.attributeValue("id")),
356                subpartEl.attributeValue("itype"),
357                subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")),
358                config,
359                parentSubpart);
360        subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false")));
361        
362        
363        for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) {
364            Element sectionEl = (Element) l.next();
365            Section section = loadSection(sectionEl, subpart, sectionTable, timetable);
366            sectionTable.put(new Long(section.getId()), section);
367        }
368        
369        return subpart;
370    }
371    
372    /**
373     * Load section
374     * @param sectionEl section element
375     * @param subpart parent subpart
376     * @param sectionTable section table (of the offering)
377     * @param timetable provided timetable
378     * @return loaded section
379     */
380    protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
381        Section parentSection = null;
382        if (sectionEl.attributeValue("parent") != null)
383            parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent")));
384        Placement placement = null;
385        if (timetable != null) {
386            placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id")));
387        } else {
388            TimeLocation time = null;
389            Element timeEl = sectionEl.element("time");
390            if (timeEl != null) {
391                time = new TimeLocation(
392                        Integer.parseInt(timeEl.attributeValue("days"), 2),
393                        Integer.parseInt(timeEl.attributeValue("start")),
394                        Integer.parseInt(timeEl.attributeValue("length")), 0, 0,
395                        timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")),
396                        timeEl.attributeValue("datePatternName", ""),
397                        createBitSet(timeEl.attributeValue("dates")),
398                        Integer.parseInt(timeEl.attributeValue("breakTime", "0")));
399                if (timeEl.attributeValue("pattern") != null)
400                    time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
401            }
402            List<RoomLocation> rooms = new ArrayList<RoomLocation>();
403            for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) {
404                Element roomEl = (Element) m.next();
405                Double posX = null, posY = null;
406                if (roomEl.attributeValue("location") != null) {
407                    String loc = roomEl.attributeValue("location");
408                    posX = Double.valueOf(loc.substring(0, loc.indexOf(',')));
409                    posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1));
410                }
411                RoomLocation room = new RoomLocation(
412                        Long.valueOf(roomEl.attributeValue("id")),
413                        roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")),
414                        roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")),
415                        0, Integer.parseInt(roomEl.attributeValue("capacity")),
416                        posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null);
417                rooms.add(room);
418            }
419            placement = (time == null ? null : new Placement(null, time, rooms));
420        }
421        
422        Section section = new Section(
423                Long.parseLong(sectionEl.attributeValue("id")),
424                Integer.parseInt(sectionEl.attributeValue("limit")),
425                sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")),
426                subpart, placement, sectionEl.attributeValue("instructorIds"),
427                sectionEl.attributeValue("instructorNames"), parentSection);
428        
429        section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0")));
430        section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0")));
431        for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) {
432            Element cNameEl = (Element)m.next();
433            section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText());
434        }
435        Element ignoreEl = sectionEl.element("no-conflicts");
436        if (ignoreEl != null) {
437            for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); )
438                section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id")));
439        }
440        
441        return section;
442    }
443    
444    /**
445     * Load reservation
446     * @param reservationEl reservation element
447     * @param offering parent offering
448     * @param configTable config table (of the offering)
449     * @param sectionTable section table (of the offering)
450     * @return loaded reservation
451     */
452    protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) {
453        Reservation r = null;
454        if ("individual".equals(reservationEl.attributeValue("type"))) {
455            Set<Long> studentIds = new HashSet<Long>();
456            for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) {
457                Element studentEl = (Element)k.next();
458                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
459            }
460            r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds);
461        } else if ("group".equals(reservationEl.attributeValue("type"))) {
462            Set<Long> studentIds = new HashSet<Long>();
463            for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) {
464                Element studentEl = (Element)k.next();
465                studentIds.add(Long.parseLong(studentEl.attributeValue("id")));
466            }
467            r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")),
468                    Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
469                    offering, studentIds);
470        } else if ("curriculum".equals(reservationEl.attributeValue("type"))) {
471            List<String> classifications = new ArrayList<String>();
472            for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) {
473                Element clasfEl = (Element)k.next();
474                classifications.add(clasfEl.attributeValue("code"));
475            }
476            List<String> majors = new ArrayList<String>();
477            for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) {
478                Element majorEl = (Element)k.next();
479                majors.add(majorEl.attributeValue("code"));
480            }
481            r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")),
482                    Double.parseDouble(reservationEl.attributeValue("limit", "-1")),
483                    offering,
484                    reservationEl.attributeValue("area"),
485                    classifications, majors);
486        } else if ("course".equals(reservationEl.attributeValue("type"))) {
487            long courseId = Long.parseLong(reservationEl.attributeValue("course"));
488            for (Course course: offering.getCourses()) {
489                if (course.getId() == courseId)
490                    r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course);
491            }
492        } else if ("dummy".equals(reservationEl.attributeValue("type"))) {
493            r = new DummyReservation(offering);
494        }
495        if (r == null) {
496            sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type"));
497            return null;
498        }
499        r.setExpired("true".equals(reservationEl.attributeValue("expired", "false")));
500        for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) {
501            Element configEl = (Element)k.next();
502            r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id"))));
503        }
504        for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) {
505            Element sectionEl = (Element)k.next();
506            r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id"))));
507        }
508        return r;
509    }
510    
511    /**
512     * Load given timetable
513     * @param timetableRoot document root in the course timetabling XML format
514     * @return loaded timetable (map class id: assigned placement)
515     */
516    protected Map<Long, Placement> loadTimetable(Element timetableRoot) {
517        Map<Long, Placement> timetable = new HashMap<Long, Placement>();
518        HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>();
519        for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) {
520            Element roomEl = (Element)i.next();
521            Long roomId = Long.valueOf(roomEl.attributeValue("id"));
522            Double posX = null, posY = null;
523            if (roomEl.attributeValue("location") != null) {
524                String loc = roomEl.attributeValue("location");
525                posX = Double.valueOf(loc.substring(0, loc.indexOf(',')));
526                posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1));
527            }
528            RoomLocation room = new RoomLocation(
529                    Long.valueOf(roomEl.attributeValue("id")),
530                    roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")),
531                    roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")),
532                    0, Integer.parseInt(roomEl.attributeValue("capacity")),
533                    posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null);
534            rooms.put(roomId, room);
535        }
536        for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) {
537            Element classEl = (Element)i.next();
538            Long classId = Long.valueOf(classEl.attributeValue("id"));
539            TimeLocation time = null;
540            Element timeEl = null;
541            for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) {
542                Element e = (Element)j.next();
543                if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; }
544            }
545            if (timeEl != null) {
546                time = new TimeLocation(
547                        Integer.parseInt(timeEl.attributeValue("days"), 2),
548                        Integer.parseInt(timeEl.attributeValue("start")),
549                        Integer.parseInt(timeEl.attributeValue("length")), 0, 0,
550                        classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")),
551                        classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")),
552                        Integer.parseInt(timeEl.attributeValue("breakTime", "0")));
553                if (timeEl.attributeValue("pattern") != null)
554                    time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
555            }
556            List<RoomLocation> room = new ArrayList<RoomLocation>();
557            for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) {
558                Element roomEl = (Element) j.next();
559                if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue;
560                room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id"))));
561            }
562            Placement placement = (time == null ? null : new Placement(null, time, room));
563            if (placement != null)
564                timetable.put(classId, placement);
565        }
566        return timetable;
567    }
568    
569    /**
570     * Load travel times
571     * @param travelTimesEl travel-time element
572     * @param metric distance metric to be populated
573     */
574    protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) {
575        for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) {
576            Element travelTimeEl = (Element)i.next();
577            metric.addTravelTime(
578                    Long.valueOf(travelTimeEl.attributeValue("id1")),
579                    Long.valueOf(travelTimeEl.attributeValue("id2")),
580                    Integer.valueOf(travelTimeEl.attributeValue("minutes")));
581        }
582    }
583    
584    /**
585     * Load linked sections
586     * @param constraintsEl constraints element
587     * @param offeringTable offering table
588     */
589    protected void loadLinkedSectiond(Element constraintsEl, Map<Long, Offering> offeringTable) {
590        for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) {
591            Element linkedEl = (Element) i.next();
592            List<Section> sections = new ArrayList<Section>();
593            for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) {
594                Element sectionEl = (Element) j.next();
595                Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering")));
596                sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id"))));
597            }
598            getModel().addLinkedSections(sections);
599        }
600    }
601    
602    /**
603     * Load students
604     * @param studentsEl students element
605     * @param offeringTable offering table
606     * @param courseTable course table
607     */
608    protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) {
609        List<Enrollment> bestEnrollments = new ArrayList<Enrollment>();
610        List<Enrollment> currentEnrollments = new ArrayList<Enrollment>();
611        for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) {
612            Element studentEl = (Element) i.next();
613            Student student = loadStudent(studentEl);
614            if (iStudentFilter != null && !iStudentFilter.accept(student))
615                continue;
616            for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) {
617                Element requestEl = (Element) j.next();
618                Request request = loadRequest(requestEl, student, offeringTable, courseTable);
619                if (request == null) continue;
620                
621                Element initialEl = requestEl.element("initial");
622                if (iLoadInitial && initialEl != null) {
623                    Enrollment enrollment = loadEnrollment(initialEl, request);
624                    if (enrollment != null)
625                        request.setInitialAssignment(enrollment);
626                }
627                Element currentEl = requestEl.element("current");
628                if (iLoadCurrent && currentEl != null) {
629                    Enrollment enrollment = loadEnrollment(currentEl, request);
630                    if (enrollment != null)
631                        currentEnrollments.add(enrollment);
632                }
633                Element bestEl = requestEl.element("best");
634                if (iLoadBest && bestEl != null) {
635                    Enrollment enrollment = loadEnrollment(bestEl, request);
636                    if (enrollment != null)
637                        bestEnrollments.add(enrollment);
638                }
639            }
640            getModel().addStudent(student);
641        }
642
643        if (!bestEnrollments.isEmpty()) {
644            // Enrollments with a reservation must go first
645            for (Enrollment enrollment : bestEnrollments) {
646                if (enrollment.getReservation() == null) continue;
647                Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment);
648                if (conflicts.isEmpty())
649                    enrollment.variable().assign(0, enrollment);
650                else
651                    sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts);
652            }
653            for (Enrollment enrollment : bestEnrollments) {
654                if (enrollment.getReservation() != null) continue;
655                Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment);
656                if (conflicts.isEmpty())
657                    enrollment.variable().assign(0, enrollment);
658                else
659                    sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts);
660            }
661            getModel().saveBest();
662        }
663
664        if (!currentEnrollments.isEmpty()) {
665            for (Request request : getModel().variables()) {
666                if (request.getAssignment() != null)
667                    request.unassign(0);
668            }
669            // Enrollments with a reservation must go first
670            for (Enrollment enrollment : currentEnrollments) {
671                if (enrollment.getReservation() == null) continue;
672                Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment);
673                if (conflicts.isEmpty())
674                    enrollment.variable().assign(0, enrollment);
675                else
676                    sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts);
677            }
678            for (Enrollment enrollment : currentEnrollments) {
679                if (enrollment.getReservation() != null) continue;
680                Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment);
681                if (conflicts.isEmpty())
682                    enrollment.variable().assign(0, enrollment);
683                else
684                    sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts);
685            }
686        }
687    }
688    
689    /**
690     * Load student
691     * @param studentEl student element
692     * @return loaded student
693     */
694    protected Student loadStudent(Element studentEl) {
695        Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy")));
696        student.setExternalId(studentEl.attributeValue("externalId"));
697        student.setName(studentEl.attributeValue("name"));
698        student.setStatus(studentEl.attributeValue("status"));
699        for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) {
700            Element requestEl = (Element) j.next();
701            if ("classification".equals(requestEl.getName())) {
702                student.getAcademicAreaClasiffications().add(
703                        new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code")));
704            } else if ("major".equals(requestEl.getName())) {
705                student.getMajors().add(
706                        new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code")));
707            } else if ("minor".equals(requestEl.getName())) {
708                student.getMinors().add(
709                        new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code")));
710            }
711        }
712        return student;
713    }
714    
715    /**
716     * Load request
717     * @param requestEl request element
718     * @param student parent student
719     * @param offeringTable offering table
720     * @param courseTable course table
721     * @return loaded request
722     */
723    protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) {
724        if ("freeTime".equals(requestEl.getName())) {
725            return loadFreeTime(requestEl, student);
726        } else if ("course".equals(requestEl.getName())) {
727            return loadCourseRequest(requestEl, student, offeringTable, courseTable);
728        } else {
729            return null;
730        }
731    }
732    
733    /**
734     * Load free time request
735     * @param requestEl request element
736     * @param student parent student
737     * @return loaded free time request
738     */
739    public FreeTimeRequest loadFreeTime(Element requestEl, Student student) {
740        TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2),
741                Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl
742                        .attributeValue("length")), 0, 0,
743                requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl
744                        .attributeValue("datePattern")), "", createBitSet(requestEl
745                        .attributeValue("dates")), 0);
746        FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")),
747                Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl
748                        .attributeValue("alternative")), student, time);
749        if (requestEl.attributeValue("weight") != null)
750            request.setWeight(Double.parseDouble(requestEl.attributeValue("weight")));
751        return request;
752    }
753    
754    /**
755     * Load course request
756     * @param requestEl request element
757     * @param student parent student
758     * @param offeringTable offering table
759     * @param courseTable course table
760     * @return loaded course request
761     */
762    public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) {
763        List<Course> courses = new ArrayList<Course>();
764        courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course"))));
765        for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();)
766            courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course"))));
767        Long timeStamp = null;
768        if (requestEl.attributeValue("timeStamp") != null)
769            timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp"));
770        CourseRequest courseRequest = new CourseRequest(
771                Long.parseLong(requestEl.attributeValue("id")),
772                Integer.parseInt(requestEl.attributeValue("priority")),
773                "true".equals(requestEl.attributeValue("alternative")), 
774                student, courses,
775                "true".equals(requestEl.attributeValue("waitlist", "false")), timeStamp);
776        if (requestEl.attributeValue("weight") != null)
777            courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight")));
778        for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) {
779            Element choiceEl = (Element) k.next();
780            courseRequest.getWaitlistedChoices().add(
781                    new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText()));
782        }
783        for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) {
784            Element choiceEl = (Element) k.next();
785            courseRequest.getSelectedChoices().add(
786                    new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText()));
787        }
788        return courseRequest;
789    }
790    
791    /**
792     * Load enrollment
793     * @param enrollmentEl enrollment element (current, best, or initial)
794     * @param request parent request
795     * @return loaded enrollment
796     */
797    protected Enrollment loadEnrollment(Element enrollmentEl, Request request) {
798        if (request instanceof CourseRequest) {
799            CourseRequest courseRequest = (CourseRequest) request;
800            HashSet<Section> sections = new HashSet<Section>();
801            for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) {
802                Element sectionEl = (Element) k.next();
803                Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id")));
804                sections.add(section);
805            }
806            Reservation reservation = null;
807            if (enrollmentEl.attributeValue("reservation", null) != null) {
808                long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation"));
809                for (Course course: courseRequest.getCourses())
810                    for (Reservation r: course.getOffering().getReservations())
811                        if (r.getId() == reservationId) { reservation = r; break; }
812            }
813            if (!sections.isEmpty())
814                return courseRequest.createEnrollment(sections, reservation);
815        } else if (request instanceof FreeTimeRequest) {
816            return ((FreeTimeRequest)request).createEnrollment();
817        }
818        return null;
819    }
820
821}