001    package net.sf.cpsolver.studentsct;
002    
003    import java.io.File;
004    import java.io.FileOutputStream;
005    import java.io.IOException;
006    import java.math.RoundingMode;
007    import java.text.DecimalFormat;
008    import java.text.DecimalFormatSymbols;
009    import java.util.BitSet;
010    import java.util.Date;
011    import java.util.Locale;
012    import java.util.Map;
013    import java.util.Set;
014    import java.util.TreeSet;
015    
016    import org.dom4j.Document;
017    import org.dom4j.DocumentHelper;
018    import org.dom4j.Element;
019    import org.dom4j.io.OutputFormat;
020    import org.dom4j.io.XMLWriter;
021    
022    import net.sf.cpsolver.coursett.IdConvertor;
023    import net.sf.cpsolver.coursett.model.RoomLocation;
024    import net.sf.cpsolver.coursett.model.TimeLocation;
025    import net.sf.cpsolver.ifs.solver.Solver;
026    import net.sf.cpsolver.ifs.util.Progress;
027    import net.sf.cpsolver.studentsct.constraint.LinkedSections;
028    import net.sf.cpsolver.studentsct.model.AcademicAreaCode;
029    import net.sf.cpsolver.studentsct.model.Choice;
030    import net.sf.cpsolver.studentsct.model.Config;
031    import net.sf.cpsolver.studentsct.model.Course;
032    import net.sf.cpsolver.studentsct.model.CourseRequest;
033    import net.sf.cpsolver.studentsct.model.Enrollment;
034    import net.sf.cpsolver.studentsct.model.FreeTimeRequest;
035    import net.sf.cpsolver.studentsct.model.Offering;
036    import net.sf.cpsolver.studentsct.model.Request;
037    import net.sf.cpsolver.studentsct.model.Section;
038    import net.sf.cpsolver.studentsct.model.Student;
039    import net.sf.cpsolver.studentsct.model.Subpart;
040    import net.sf.cpsolver.studentsct.reservation.CourseReservation;
041    import net.sf.cpsolver.studentsct.reservation.CurriculumReservation;
042    import net.sf.cpsolver.studentsct.reservation.DummyReservation;
043    import net.sf.cpsolver.studentsct.reservation.GroupReservation;
044    import net.sf.cpsolver.studentsct.reservation.IndividualReservation;
045    import net.sf.cpsolver.studentsct.reservation.Reservation;
046    
047    /**
048     * Save student sectioning solution into an XML file.
049     * 
050     * <br>
051     * <br>
052     * Parameters:
053     * <table border='1'>
054     * <tr>
055     * <th>Parameter</th>
056     * <th>Type</th>
057     * <th>Comment</th>
058     * </tr>
059     * <tr>
060     * <td>General.Output</td>
061     * <td>{@link String}</td>
062     * <td>Folder with the output solution in XML format (solution.xml)</td>
063     * </tr>
064     * <tr>
065     * <td>Xml.ConvertIds</td>
066     * <td>{@link Boolean}</td>
067     * <td>If true, ids are converted (to be able to make input data public)</td>
068     * </tr>
069     * <tr>
070     * <td>Xml.ShowNames</td>
071     * <td>{@link Boolean}</td>
072     * <td>If false, names are not exported (to be able to make input data public)</td>
073     * </tr>
074     * <tr>
075     * <td>Xml.SaveBest</td>
076     * <td>{@link Boolean}</td>
077     * <td>If true, best solution is saved.</td>
078     * </tr>
079     * <tr>
080     * <td>Xml.SaveInitial</td>
081     * <td>{@link Boolean}</td>
082     * <td>If true, initial solution is saved.</td>
083     * </tr>
084     * <tr>
085     * <td>Xml.SaveCurrent</td>
086     * <td>{@link Boolean}</td>
087     * <td>If true, current solution is saved.</td>
088     * </tr>
089     * <tr>
090     * <td>Xml.SaveOnlineSectioningInfo</td>
091     * <td>{@link Boolean}</td>
092     * <td>If true, save online sectioning info (i.e., expected and held space of
093     * each section)</td>
094     * </tr>
095     * <tr>
096     * <td>Xml.SaveStudentInfo</td>
097     * <td>{@link Boolean}</td>
098     * <td>If true, save student information (i.e., academic area classification,
099     * major, minor)</td>
100     * </tr>
101     * </table>
102     * <br>
103     * <br>
104     * Usage:<br>
105     * <code>
106     * new StudentSectioningXMLSaver(solver).save(new File("solution.xml"));<br> 
107     * </code>
108     * 
109     * @version StudentSct 1.2 (Student Sectioning)<br>
110     *          Copyright (C) 2007 - 2010 Tomas Muller<br>
111     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
112     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
113     * <br>
114     *          This library is free software; you can redistribute it and/or modify
115     *          it under the terms of the GNU Lesser General Public License as
116     *          published by the Free Software Foundation; either version 3 of the
117     *          License, or (at your option) any later version. <br>
118     * <br>
119     *          This library is distributed in the hope that it will be useful, but
120     *          WITHOUT ANY WARRANTY; without even the implied warranty of
121     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
122     *          Lesser General Public License for more details. <br>
123     * <br>
124     *          You should have received a copy of the GNU Lesser General Public
125     *          License along with this library; if not see
126     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
127     */
128    
129    public class StudentSectioningXMLSaver extends StudentSectioningSaver {
130        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(StudentSectioningXMLSaver.class);
131        private static DecimalFormat[] sDF = { new DecimalFormat(""), new DecimalFormat("0"), new DecimalFormat("00"),
132                new DecimalFormat("000"), new DecimalFormat("0000"), new DecimalFormat("00000"),
133                new DecimalFormat("000000"), new DecimalFormat("0000000") };
134        private static DecimalFormat sStudentWeightFormat = new DecimalFormat("0.0000", new DecimalFormatSymbols(Locale.US));
135        private File iOutputFolder = null;
136    
137        private boolean iSaveBest = false;
138        private boolean iSaveInitial = false;
139        private boolean iSaveCurrent = false;
140        private boolean iSaveOnlineSectioningInfo = false;
141        private boolean iSaveStudentInfo = true;
142    
143        private boolean iConvertIds = false;
144        private boolean iShowNames = false;
145        
146        static {
147            sStudentWeightFormat.setRoundingMode(RoundingMode.DOWN);
148        }
149    
150        /**
151         * Constructor
152         * 
153         * @param solver
154         *            student sectioning solver
155         */
156        public StudentSectioningXMLSaver(Solver<Request, Enrollment> solver) {
157            super(solver);
158            iOutputFolder = new File(getModel().getProperties().getProperty("General.Output",
159                    "." + File.separator + "output"));
160            iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", true);
161            iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", true);
162            iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", false);
163            iSaveOnlineSectioningInfo = getModel().getProperties().getPropertyBoolean("Xml.SaveOnlineSectioningInfo", true);
164            iSaveStudentInfo = getModel().getProperties().getPropertyBoolean("Xml.SaveStudentInfo", true);
165            iShowNames = getModel().getProperties().getPropertyBoolean("Xml.ShowNames", true);
166            iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds", false);
167        }
168    
169        /** Convert bitset to a bit string */
170        private static String bitset2string(BitSet b) {
171            StringBuffer sb = new StringBuffer();
172            for (int i = 0; i < b.length(); i++)
173                sb.append(b.get(i) ? "1" : "0");
174            return sb.toString();
175        }
176    
177        /** Generate id for given object with the given id */
178        private String getId(String type, String id) {
179            if (!iConvertIds)
180                return id.toString();
181            return IdConvertor.getInstance().convert(type, id);
182        }
183    
184        /** Generate id for given object with the given id */
185        private String getId(String type, Number id) {
186            return getId(type, id.toString());
187        }
188    
189        /** Generate id for given object with the given id */
190        private String getId(String type, long id) {
191            return getId(type, String.valueOf(id));
192        }
193    
194        /** Save an XML file */
195        @Override
196        public void save() throws Exception {
197            save(null);
198        }
199    
200        /**
201         * Save an XML file
202         * 
203         * @param outFile
204         *            output file
205         */
206        public void save(File outFile) throws Exception {
207            if (outFile == null) {
208                outFile = new File(iOutputFolder, "solution.xml");
209            } else if (outFile.getParentFile() != null) {
210                outFile.getParentFile().mkdirs();
211            }
212            sLogger.debug("Writting XML data to:" + outFile);
213    
214            Document document = DocumentHelper.createDocument();
215            document.addComment("Student Sectioning");
216    
217            if ((iSaveCurrent || iSaveBest)) { // &&
218                                               // !getModel().assignedVariables().isEmpty()
219                StringBuffer comments = new StringBuffer("Solution Info:\n");
220                Map<String, String> solutionInfo = (getSolution() == null ? getModel().getExtendedInfo() : getSolution()
221                        .getExtendedInfo());
222                for (String key : new TreeSet<String>(solutionInfo.keySet())) {
223                    String value = solutionInfo.get(key);
224                    comments.append("    " + key + ": " + value + "\n");
225                }
226                document.addComment(comments.toString());
227            }
228    
229            Element root = document.addElement("sectioning");
230            root.addAttribute("version", "1.0");
231            root.addAttribute("initiative", getModel().getProperties().getProperty("Data.Initiative"));
232            root.addAttribute("term", getModel().getProperties().getProperty("Data.Term"));
233            root.addAttribute("year", getModel().getProperties().getProperty("Data.Year"));
234            root.addAttribute("created", String.valueOf(new Date()));
235    
236            Element offeringsEl = root.addElement("offerings");
237            for (Offering offering : getModel().getOfferings()) {
238                Element offeringEl = offeringsEl.addElement("offering");
239                offeringEl.addAttribute("id", getId("offering", offering.getId()));
240                if (iShowNames)
241                    offeringEl.addAttribute("name", offering.getName());
242                for (Course course : offering.getCourses()) {
243                    Element courseEl = offeringEl.addElement("course");
244                    courseEl.addAttribute("id", getId("course", course.getId()));
245                    if (iShowNames)
246                        courseEl.addAttribute("subjectArea", course.getSubjectArea());
247                    if (iShowNames)
248                        courseEl.addAttribute("courseNbr", course.getCourseNumber());
249                    if (iShowNames && course.getLimit() >= 0)
250                        courseEl.addAttribute("limit", String.valueOf(course.getLimit()));
251                    if (iShowNames && course.getProjected() != 0)
252                        courseEl.addAttribute("projected", String.valueOf(course.getProjected()));
253                }
254                for (Config config : offering.getConfigs()) {
255                    Element configEl = offeringEl.addElement("config");
256                    configEl.addAttribute("id", getId("config", config.getId()));
257                    if (config.getLimit() >= 0)
258                        configEl.addAttribute("limit", String.valueOf(config.getLimit()));
259                    if (iShowNames)
260                        configEl.addAttribute("name", config.getName());
261                    for (Subpart subpart : config.getSubparts()) {
262                        Element subpartEl = configEl.addElement("subpart");
263                        subpartEl.addAttribute("id", getId("subpart", subpart.getId()));
264                        subpartEl.addAttribute("itype", subpart.getInstructionalType());
265                        if (subpart.getParent() != null)
266                            subpartEl.addAttribute("parent", getId("subpart", subpart.getParent().getId()));
267                        if (iShowNames)
268                            subpartEl.addAttribute("name", subpart.getName());
269                        if (subpart.isAllowOverlap())
270                            subpartEl.addAttribute("allowOverlap", "true");
271                        for (Section section : subpart.getSections()) {
272                            Element sectionEl = subpartEl.addElement("section");
273                            sectionEl.addAttribute("id", getId("section", section.getId()));
274                            sectionEl.addAttribute("limit", String.valueOf(section.getLimit()));
275                            if (section.getNameByCourse() != null)
276                                for (Map.Entry<Long, String> entry: section.getNameByCourse().entrySet())
277                                    sectionEl.addElement("cname").addAttribute("id", entry.getKey().toString()).setText(entry.getValue());
278                            if (section.getParent() != null)
279                                sectionEl.addAttribute("parent", getId("section", section.getParent().getId()));
280                            if (iShowNames && section.getChoice().getInstructorIds() != null)
281                                sectionEl.addAttribute("instructorIds", section.getChoice().getInstructorIds());
282                            if (iShowNames && section.getChoice().getInstructorNames() != null)
283                                sectionEl.addAttribute("instructorNames", section.getChoice().getInstructorNames());
284                            if (iShowNames)
285                                sectionEl.addAttribute("name", section.getName());
286                            if (section.getPlacement() != null) {
287                                TimeLocation tl = section.getPlacement().getTimeLocation();
288                                if (tl != null) {
289                                    Element timeLocationEl = sectionEl.addElement("time");
290                                    timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer
291                                            .toBinaryString(tl.getDayCode()))));
292                                    timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
293                                    timeLocationEl.addAttribute("length", String.valueOf(tl.getLength()));
294                                    if (tl.getBreakTime() != 0)
295                                        timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
296                                    if (iShowNames && tl.getTimePatternId() != null)
297                                        timeLocationEl.addAttribute("pattern", getId("timePattern", tl.getTimePatternId()));
298                                    if (iShowNames && tl.getDatePatternId() != null)
299                                        timeLocationEl.addAttribute("datePattern", tl.getDatePatternId().toString());
300                                    if (iShowNames && tl.getDatePatternName() != null
301                                            && tl.getDatePatternName().length() > 0)
302                                        timeLocationEl.addAttribute("datePatternName", tl.getDatePatternName());
303                                    timeLocationEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
304                                    if (iShowNames)
305                                        timeLocationEl.setText(tl.getLongName());
306                                }
307                                for (RoomLocation rl : section.getRooms()) {
308                                    Element roomLocationEl = sectionEl.addElement("room");
309                                    roomLocationEl.addAttribute("id", getId("room", rl.getId()));
310                                    if (iShowNames && rl.getBuildingId() != null)
311                                        roomLocationEl.addAttribute("building", getId("building", rl.getBuildingId()));
312                                    if (iShowNames && rl.getName() != null)
313                                        roomLocationEl.addAttribute("name", rl.getName());
314                                    roomLocationEl.addAttribute("capacity", String.valueOf(rl.getRoomSize()));
315                                    if (rl.getPosX() != null && rl.getPosY() != null)
316                                        roomLocationEl.addAttribute("location", rl.getPosX() + "," + rl.getPosY());
317                                    if (rl.getIgnoreTooFar())
318                                        roomLocationEl.addAttribute("ignoreTooFar", "true");
319                                }
320                            }
321                            if (iSaveOnlineSectioningInfo) {
322                                if (section.getSpaceHeld() != 0.0)
323                                    sectionEl.addAttribute("hold", sStudentWeightFormat.format(section.getSpaceHeld()));
324                                if (section.getSpaceExpected() != 0.0)
325                                    sectionEl.addAttribute("expect", sStudentWeightFormat
326                                            .format(section.getSpaceExpected()));
327                            }
328                            if (section.getIgnoreConflictWithSectionIds() != null && !section.getIgnoreConflictWithSectionIds().isEmpty()) {
329                                Element ignoreEl = sectionEl.addElement("no-conflicts");
330                                for (Long sectionId: section.getIgnoreConflictWithSectionIds())
331                                    ignoreEl.addElement("section").addAttribute("id", getId("section", sectionId));
332                            }
333                        }
334                    }
335                }
336                if (!offering.getReservations().isEmpty()) {
337                    for (Reservation r: offering.getReservations()) {
338                        Element reservationEl = offeringEl.addElement("reservation");
339                        reservationEl.addAttribute("id", getId("reservation", r.getId()));
340                        reservationEl.addAttribute("expired", r.isExpired() ? "true" : "false");
341                        if (r instanceof GroupReservation) {
342                            GroupReservation gr = (GroupReservation)r;
343                            reservationEl.addAttribute("type", "group");
344                            for (Long studentId: gr.getStudentIds())
345                                reservationEl.addElement("student").addAttribute("id", getId("student", studentId));
346                            if (gr.getReservationLimit() >= 0.0)
347                                reservationEl.addAttribute("limit", String.valueOf(gr.getReservationLimit()));
348                        } else if (r instanceof IndividualReservation) {
349                            reservationEl.addAttribute("type", "individual");
350                            for (Long studentId: ((IndividualReservation)r).getStudentIds())
351                                reservationEl.addElement("student").addAttribute("id", getId("student", studentId));
352                        } else if (r instanceof CurriculumReservation) {
353                            reservationEl.addAttribute("type", "curriculum");
354                            CurriculumReservation cr = (CurriculumReservation)r;
355                            if (cr.getReservationLimit() >= 0.0)
356                                reservationEl.addAttribute("limit", String.valueOf(cr.getReservationLimit()));
357                            reservationEl.addAttribute("area", cr.getAcademicArea());
358                            for (String clasf: cr.getClassifications())
359                                reservationEl.addElement("classification").addAttribute("code", clasf);
360                            for (String major: cr.getMajors())
361                                reservationEl.addElement("major").addAttribute("code", major);
362                        } else if (r instanceof CourseReservation) {
363                            reservationEl.addAttribute("type", "course");
364                            CourseReservation cr = (CourseReservation)r;
365                            reservationEl.addAttribute("course", getId("course",cr.getCourse().getId()));
366                        } else if (r instanceof DummyReservation) {
367                            reservationEl.addAttribute("type", "dummy");
368                        }
369                        for (Config config: r.getConfigs())
370                            reservationEl.addElement("config").addAttribute("id", getId("config", config.getId()));
371                        for (Map.Entry<Subpart, Set<Section>> entry: r.getSections().entrySet()) {
372                            for (Section section: entry.getValue()) {
373                                reservationEl.addElement("section").addAttribute("id", getId("section", section.getId()));
374                            }
375                        }
376                    }
377                }
378            }
379    
380            Element studentsEl = root.addElement("students");
381            for (Student student : getModel().getStudents()) {
382                Element studentEl = studentsEl.addElement("student");
383                studentEl.addAttribute("id", getId("student", student.getId()));
384                if (iShowNames) {
385                    if (student.getExternalId() != null && !student.getExternalId().isEmpty())
386                        studentEl.addAttribute("externalId", student.getExternalId());
387                    if (student.getName() != null && !student.getName().isEmpty())
388                        studentEl.addAttribute("name", student.getName());
389                    if (student.getStatus() != null && !student.getStatus().isEmpty())
390                        studentEl.addAttribute("status", student.getStatus());
391                }
392                if (student.isDummy())
393                    studentEl.addAttribute("dummy", "true");
394                if (iSaveStudentInfo) {
395                    for (AcademicAreaCode aac : student.getAcademicAreaClasiffications()) {
396                        Element aacEl = studentEl.addElement("classification");
397                        if (aac.getArea() != null)
398                            aacEl.addAttribute("area", aac.getArea());
399                        if (aac.getCode() != null)
400                            aacEl.addAttribute("code", aac.getCode());
401                    }
402                    for (AcademicAreaCode aac : student.getMajors()) {
403                        Element aacEl = studentEl.addElement("major");
404                        if (aac.getArea() != null)
405                            aacEl.addAttribute("area", aac.getArea());
406                        if (aac.getCode() != null)
407                            aacEl.addAttribute("code", aac.getCode());
408                    }
409                    for (AcademicAreaCode aac : student.getMinors()) {
410                        Element aacEl = studentEl.addElement("minor");
411                        if (aac.getArea() != null)
412                            aacEl.addAttribute("area", aac.getArea());
413                        if (aac.getCode() != null)
414                            aacEl.addAttribute("code", aac.getCode());
415                    }
416                }
417                for (Request request : student.getRequests()) {
418                    if (request instanceof FreeTimeRequest) {
419                        Element requestEl = studentEl.addElement("freeTime");
420                        FreeTimeRequest ft = (FreeTimeRequest) request;
421                        requestEl.addAttribute("id", getId("request", request.getId()));
422                        requestEl.addAttribute("priority", String.valueOf(request.getPriority()));
423                        if (request.isAlternative())
424                            requestEl.addAttribute("alternative", "true");
425                        if (request.getWeight() != 1.0)
426                            requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight()));
427                        TimeLocation tl = ft.getTime();
428                        if (tl != null) {
429                            requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl
430                                    .getDayCode()))));
431                            requestEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
432                            requestEl.addAttribute("length", String.valueOf(tl.getLength()));
433                            if (iShowNames && tl.getDatePatternId() != null)
434                                requestEl.addAttribute("datePattern", tl.getDatePatternId().toString());
435                            requestEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
436                            if (iShowNames)
437                                requestEl.setText(tl.getLongName());
438                        }
439                        if (iSaveInitial && request.getInitialAssignment() != null) {
440                            requestEl.addElement("initial");
441                        }
442                        if (iSaveCurrent && request.getAssignment() != null) {
443                            requestEl.addElement("current");
444                        }
445                        if (iSaveBest && request.getBestAssignment() != null) {
446                            requestEl.addElement("best");
447                        }
448                    } else if (request instanceof CourseRequest) {
449                        CourseRequest cr = (CourseRequest) request;
450                        Element requestEl = studentEl.addElement("course");
451                        requestEl.addAttribute("id", getId("request", request.getId()));
452                        requestEl.addAttribute("priority", String.valueOf(request.getPriority()));
453                        if (request.isAlternative())
454                            requestEl.addAttribute("alternative", "true");
455                        if (request.getWeight() != 1.0)
456                            requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight()));
457                        requestEl.addAttribute("waitlist", cr.isWaitlist() ? "true" : "false");
458                        if (cr.getTimeStamp() != null)
459                            requestEl.addAttribute("timeStamp", cr.getTimeStamp().toString());
460                        boolean first = true;
461                        for (Course course : cr.getCourses()) {
462                            if (first)
463                                requestEl.addAttribute("course", getId("course", course.getId()));
464                            else
465                                requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId()));
466                            first = false;
467                        }
468                        for (Choice choice : cr.getWaitlistedChoices()) {
469                            Element choiceEl = requestEl.addElement("waitlisted");
470                            choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId()));
471                            choiceEl.setText(choice.getId());
472                        }
473                        for (Choice choice : cr.getSelectedChoices()) {
474                            Element choiceEl = requestEl.addElement("selected");
475                            choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId()));
476                            choiceEl.setText(choice.getId());
477                        }
478                        if (iSaveInitial && request.getInitialAssignment() != null) {
479                            Element assignmentEl = requestEl.addElement("initial");
480                            Enrollment enrollment = request.getInitialAssignment();
481                            if (enrollment.getReservation() != null)
482                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
483                            for (Section section : enrollment.getSections()) {
484                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
485                                        getId("section", section.getId()));
486                                if (iShowNames)
487                                    sectionEl.setText(section.getName()
488                                            + " "
489                                            + (section.getTime() == null ? " Arr Hrs" : " "
490                                                    + section.getTime().getLongName())
491                                            + (section.getNrRooms() == 0 ? "" : " "
492                                                    + section.getPlacement().getRoomName(","))
493                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
494                                                    + section.getChoice().getInstructorNames()));
495                            }
496                        }
497                        if (iSaveCurrent && request.getAssignment() != null) {
498                            Element assignmentEl = requestEl.addElement("current");
499                            Enrollment enrollment = request.getAssignment();
500                            if (enrollment.getReservation() != null)
501                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
502                            for (Section section : enrollment.getSections()) {
503                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
504                                        getId("section", section.getId()));
505                                if (iShowNames)
506                                    sectionEl.setText(section.getName()
507                                            + " "
508                                            + (section.getTime() == null ? " Arr Hrs" : " "
509                                                    + section.getTime().getLongName())
510                                            + (section.getNrRooms() == 0 ? "" : " "
511                                                    + section.getPlacement().getRoomName(","))
512                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
513                                                    + section.getChoice().getInstructorNames()));
514                            }
515                        }
516                        if (iSaveBest && request.getBestAssignment() != null) {
517                            Element assignmentEl = requestEl.addElement("best");
518                            Enrollment enrollment = request.getBestAssignment();
519                            if (enrollment.getReservation() != null)
520                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
521                            for (Section section : enrollment.getSections()) {
522                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
523                                        getId("section", section.getId()));
524                                if (iShowNames)
525                                    sectionEl.setText(section.getName()
526                                            + " "
527                                            + (section.getTime() == null ? " Arr Hrs" : " "
528                                                    + section.getTime().getLongName())
529                                            + (section.getNrRooms() == 0 ? "" : " "
530                                                    + section.getPlacement().getRoomName(","))
531                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
532                                                    + section.getChoice().getInstructorNames()));
533                            }
534                        }
535                    }
536                }
537            }
538            
539            Element constrainstEl = root.addElement("constraints");
540            for (LinkedSections linkedSections: getModel().getLinkedSections()) {
541                Element linkEl = constrainstEl.addElement("linked-sections");
542                for (Offering offering: linkedSections.getOfferings())
543                    for (Subpart subpart: linkedSections.getSubparts(offering))
544                        for (Section section: linkedSections.getSections(subpart))
545                            linkEl.addElement("section")
546                                .addAttribute("offering", getId("offering", offering.getId()))
547                                .addAttribute("id", getId("section", section.getId()));
548            }
549            
550            if (getModel().getDistanceConflict() != null) {
551                Map<Long, Map<Long, Integer>> travelTimes = getModel().getDistanceConflict().getDistanceMetric().getTravelTimes();
552                if (travelTimes != null) {
553                    Element travelTimesEl = root.addElement("travel-times");
554                    for (Map.Entry<Long, Map<Long, Integer>> e1: travelTimes.entrySet())
555                        for (Map.Entry<Long, Integer> e2: e1.getValue().entrySet())
556                            travelTimesEl.addElement("travel-time")
557                                .addAttribute("id1", getId("room", e1.getKey().toString()))
558                                .addAttribute("id2", getId("room", e2.getKey().toString()))
559                                .addAttribute("minutes", e2.getValue().toString());
560                }
561            }
562    
563    
564            if (iShowNames) {
565                Progress.getInstance(getModel()).save(root);
566            }
567    
568            FileOutputStream fos = null;
569            try {
570                fos = new FileOutputStream(outFile);
571                (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(document);
572                fos.flush();
573                fos.close();
574                fos = null;
575            } finally {
576                try {
577                    if (fos != null)
578                        fos.close();
579                } catch (IOException e) {
580                }
581            }
582    
583            if (iConvertIds)
584                IdConvertor.getInstance().save();
585        }
586    
587    }