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