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            outFile.getParentFile().mkdirs();
208            sLogger.debug("Writting XML data to:" + outFile);
209    
210            Document document = DocumentHelper.createDocument();
211            document.addComment("Student Sectioning");
212    
213            if ((iSaveCurrent || iSaveBest)) { // &&
214                                               // !getModel().assignedVariables().isEmpty()
215                StringBuffer comments = new StringBuffer("Solution Info:\n");
216                Map<String, String> solutionInfo = (getSolution() == null ? getModel().getExtendedInfo() : getSolution()
217                        .getExtendedInfo());
218                for (String key : new TreeSet<String>(solutionInfo.keySet())) {
219                    String value = solutionInfo.get(key);
220                    comments.append("    " + key + ": " + value + "\n");
221                }
222                document.addComment(comments.toString());
223            }
224    
225            Element root = document.addElement("sectioning");
226            root.addAttribute("version", "1.0");
227            root.addAttribute("initiative", getModel().getProperties().getProperty("Data.Initiative"));
228            root.addAttribute("term", getModel().getProperties().getProperty("Data.Term"));
229            root.addAttribute("year", getModel().getProperties().getProperty("Data.Year"));
230            root.addAttribute("created", String.valueOf(new Date()));
231    
232            Element offeringsEl = root.addElement("offerings");
233            for (Offering offering : getModel().getOfferings()) {
234                Element offeringEl = offeringsEl.addElement("offering");
235                offeringEl.addAttribute("id", getId("offering", offering.getId()));
236                if (iShowNames)
237                    offeringEl.addAttribute("name", offering.getName());
238                for (Course course : offering.getCourses()) {
239                    Element courseEl = offeringEl.addElement("course");
240                    courseEl.addAttribute("id", getId("course", course.getId()));
241                    if (iShowNames)
242                        courseEl.addAttribute("subjectArea", course.getSubjectArea());
243                    if (iShowNames)
244                        courseEl.addAttribute("courseNbr", course.getCourseNumber());
245                    if (iShowNames && course.getLimit() >= 0)
246                        courseEl.addAttribute("limit", String.valueOf(course.getLimit()));
247                    if (iShowNames && course.getProjected() != 0)
248                        courseEl.addAttribute("projected", String.valueOf(course.getProjected()));
249                }
250                for (Config config : offering.getConfigs()) {
251                    Element configEl = offeringEl.addElement("config");
252                    configEl.addAttribute("id", getId("config", config.getId()));
253                    if (config.getLimit() >= 0)
254                        configEl.addAttribute("limit", String.valueOf(config.getLimit()));
255                    if (iShowNames)
256                        configEl.addAttribute("name", config.getName());
257                    for (Subpart subpart : config.getSubparts()) {
258                        Element subpartEl = configEl.addElement("subpart");
259                        subpartEl.addAttribute("id", getId("subpart", subpart.getId()));
260                        subpartEl.addAttribute("itype", subpart.getInstructionalType());
261                        if (subpart.getParent() != null)
262                            subpartEl.addAttribute("parent", getId("subpart", subpart.getParent().getId()));
263                        if (iShowNames)
264                            subpartEl.addAttribute("name", subpart.getName());
265                        if (subpart.isAllowOverlap())
266                            subpartEl.addAttribute("allowOverlap", "true");
267                        for (Section section : subpart.getSections()) {
268                            Element sectionEl = subpartEl.addElement("section");
269                            sectionEl.addAttribute("id", getId("section", section.getId()));
270                            sectionEl.addAttribute("limit", String.valueOf(section.getLimit()));
271                            if (section.getNameByCourse() != null)
272                                for (Map.Entry<Long, String> entry: section.getNameByCourse().entrySet())
273                                    sectionEl.addElement("cname").addAttribute("id", entry.getKey().toString()).setText(entry.getValue());
274                            if (section.getParent() != null)
275                                sectionEl.addAttribute("parent", getId("section", section.getParent().getId()));
276                            if (iShowNames && section.getChoice().getInstructorIds() != null)
277                                sectionEl.addAttribute("instructorIds", section.getChoice().getInstructorIds());
278                            if (iShowNames && section.getChoice().getInstructorNames() != null)
279                                sectionEl.addAttribute("instructorNames", section.getChoice().getInstructorNames());
280                            if (iShowNames)
281                                sectionEl.addAttribute("name", section.getName());
282                            if (section.getPlacement() != null) {
283                                TimeLocation tl = section.getPlacement().getTimeLocation();
284                                if (tl != null) {
285                                    Element timeLocationEl = sectionEl.addElement("time");
286                                    timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer
287                                            .toBinaryString(tl.getDayCode()))));
288                                    timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
289                                    timeLocationEl.addAttribute("length", String.valueOf(tl.getLength()));
290                                    if (tl.getBreakTime() != 0)
291                                        timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
292                                    if (iShowNames && tl.getTimePatternId() != null)
293                                        timeLocationEl.addAttribute("pattern", getId("timePattern", tl.getTimePatternId()));
294                                    if (iShowNames && tl.getDatePatternId() != null)
295                                        timeLocationEl.addAttribute("datePattern", tl.getDatePatternId().toString());
296                                    if (iShowNames && tl.getDatePatternName() != null
297                                            && tl.getDatePatternName().length() > 0)
298                                        timeLocationEl.addAttribute("datePatternName", tl.getDatePatternName());
299                                    timeLocationEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
300                                    if (iShowNames)
301                                        timeLocationEl.setText(tl.getLongName());
302                                }
303                                for (RoomLocation rl : section.getRooms()) {
304                                    Element roomLocationEl = sectionEl.addElement("room");
305                                    roomLocationEl.addAttribute("id", getId("room", rl.getId()));
306                                    if (iShowNames && rl.getBuildingId() != null)
307                                        roomLocationEl.addAttribute("building", getId("building", rl.getBuildingId()));
308                                    if (iShowNames && rl.getName() != null)
309                                        roomLocationEl.addAttribute("name", rl.getName());
310                                    roomLocationEl.addAttribute("capacity", String.valueOf(rl.getRoomSize()));
311                                    if (rl.getPosX() != null && rl.getPosY() != null)
312                                        roomLocationEl.addAttribute("location", rl.getPosX() + "," + rl.getPosY());
313                                    if (rl.getIgnoreTooFar())
314                                        roomLocationEl.addAttribute("ignoreTooFar", "true");
315                                }
316                            }
317                            if (iSaveOnlineSectioningInfo) {
318                                if (section.getSpaceHeld() != 0.0)
319                                    sectionEl.addAttribute("hold", sStudentWeightFormat.format(section.getSpaceHeld()));
320                                if (section.getSpaceExpected() != 0.0)
321                                    sectionEl.addAttribute("expect", sStudentWeightFormat
322                                            .format(section.getSpaceExpected()));
323                            }
324                            if (section.getIgnoreConflictWithSectionIds() != null && !section.getIgnoreConflictWithSectionIds().isEmpty()) {
325                                Element ignoreEl = sectionEl.addElement("no-conflicts");
326                                for (Long sectionId: section.getIgnoreConflictWithSectionIds())
327                                    ignoreEl.addElement("section").addAttribute("id", getId("section", sectionId));
328                            }
329                        }
330                    }
331                }
332                if (!offering.getReservations().isEmpty()) {
333                    for (Reservation r: offering.getReservations()) {
334                        Element reservationEl = offeringEl.addElement("reservation");
335                        reservationEl.addAttribute("id", getId("reservation", r.getId()));
336                        reservationEl.addAttribute("expired", r.isExpired() ? "true" : "false");
337                        if (r instanceof GroupReservation) {
338                            GroupReservation gr = (GroupReservation)r;
339                            reservationEl.addAttribute("type", "group");
340                            for (Long studentId: gr.getStudentIds())
341                                reservationEl.addElement("student").addAttribute("id", getId("student", studentId));
342                            if (gr.getReservationLimit() >= 0.0)
343                                reservationEl.addAttribute("limit", String.valueOf(gr.getReservationLimit()));
344                        } else if (r instanceof IndividualReservation) {
345                            reservationEl.addAttribute("type", "individual");
346                            for (Long studentId: ((IndividualReservation)r).getStudentIds())
347                                reservationEl.addElement("student").addAttribute("id", getId("student", studentId));
348                        } else if (r instanceof CurriculumReservation) {
349                            reservationEl.addAttribute("type", "curriculum");
350                            CurriculumReservation cr = (CurriculumReservation)r;
351                            if (cr.getReservationLimit() >= 0.0)
352                                reservationEl.addAttribute("limit", String.valueOf(cr.getReservationLimit()));
353                            reservationEl.addAttribute("area", cr.getAcademicArea());
354                            for (String clasf: cr.getClassifications())
355                                reservationEl.addElement("classification").addAttribute("code", clasf);
356                            for (String major: cr.getMajors())
357                                reservationEl.addElement("major").addAttribute("code", major);
358                        } else if (r instanceof CourseReservation) {
359                            reservationEl.addAttribute("type", "course");
360                            CourseReservation cr = (CourseReservation)r;
361                            reservationEl.addAttribute("course", getId("course",cr.getCourse().getId()));
362                        } else if (r instanceof DummyReservation) {
363                            reservationEl.addAttribute("type", "dummy");
364                        }
365                        for (Config config: r.getConfigs())
366                            reservationEl.addElement("config").addAttribute("id", getId("config", config.getId()));
367                        for (Map.Entry<Subpart, Set<Section>> entry: r.getSections().entrySet()) {
368                            for (Section section: entry.getValue()) {
369                                reservationEl.addElement("section").addAttribute("id", getId("section", section.getId()));
370                            }
371                        }
372                    }
373                }
374            }
375    
376            Element studentsEl = root.addElement("students");
377            for (Student student : getModel().getStudents()) {
378                Element studentEl = studentsEl.addElement("student");
379                studentEl.addAttribute("id", getId("student", student.getId()));
380                if (iShowNames) {
381                    if (student.getExternalId() != null && !student.getExternalId().isEmpty())
382                        studentEl.addAttribute("externalId", student.getExternalId());
383                    if (student.getName() != null && !student.getName().isEmpty())
384                        studentEl.addAttribute("name", student.getName());
385                    if (student.getStatus() != null && !student.getStatus().isEmpty())
386                        studentEl.addAttribute("status", student.getStatus());
387                }
388                if (student.isDummy())
389                    studentEl.addAttribute("dummy", "true");
390                if (iSaveStudentInfo) {
391                    for (AcademicAreaCode aac : student.getAcademicAreaClasiffications()) {
392                        Element aacEl = studentEl.addElement("classification");
393                        if (aac.getArea() != null)
394                            aacEl.addAttribute("area", aac.getArea());
395                        if (aac.getCode() != null)
396                            aacEl.addAttribute("code", aac.getCode());
397                    }
398                    for (AcademicAreaCode aac : student.getMajors()) {
399                        Element aacEl = studentEl.addElement("major");
400                        if (aac.getArea() != null)
401                            aacEl.addAttribute("area", aac.getArea());
402                        if (aac.getCode() != null)
403                            aacEl.addAttribute("code", aac.getCode());
404                    }
405                    for (AcademicAreaCode aac : student.getMinors()) {
406                        Element aacEl = studentEl.addElement("minor");
407                        if (aac.getArea() != null)
408                            aacEl.addAttribute("area", aac.getArea());
409                        if (aac.getCode() != null)
410                            aacEl.addAttribute("code", aac.getCode());
411                    }
412                }
413                for (Request request : student.getRequests()) {
414                    if (request instanceof FreeTimeRequest) {
415                        Element requestEl = studentEl.addElement("freeTime");
416                        FreeTimeRequest ft = (FreeTimeRequest) request;
417                        requestEl.addAttribute("id", getId("request", request.getId()));
418                        requestEl.addAttribute("priority", String.valueOf(request.getPriority()));
419                        if (request.isAlternative())
420                            requestEl.addAttribute("alternative", "true");
421                        if (request.getWeight() != 1.0)
422                            requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight()));
423                        TimeLocation tl = ft.getTime();
424                        if (tl != null) {
425                            requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl
426                                    .getDayCode()))));
427                            requestEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
428                            requestEl.addAttribute("length", String.valueOf(tl.getLength()));
429                            if (iShowNames && tl.getDatePatternId() != null)
430                                requestEl.addAttribute("datePattern", tl.getDatePatternId().toString());
431                            requestEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
432                            if (iShowNames)
433                                requestEl.setText(tl.getLongName());
434                        }
435                        if (iSaveInitial && request.getInitialAssignment() != null) {
436                            requestEl.addElement("initial");
437                        }
438                        if (iSaveCurrent && request.getAssignment() != null) {
439                            requestEl.addElement("current");
440                        }
441                        if (iSaveBest && request.getBestAssignment() != null) {
442                            requestEl.addElement("best");
443                        }
444                    } else if (request instanceof CourseRequest) {
445                        CourseRequest cr = (CourseRequest) request;
446                        Element requestEl = studentEl.addElement("course");
447                        requestEl.addAttribute("id", getId("request", request.getId()));
448                        requestEl.addAttribute("priority", String.valueOf(request.getPriority()));
449                        if (request.isAlternative())
450                            requestEl.addAttribute("alternative", "true");
451                        if (request.getWeight() != 1.0)
452                            requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight()));
453                        requestEl.addAttribute("waitlist", cr.isWaitlist() ? "true" : "false");
454                        if (cr.getTimeStamp() != null)
455                            requestEl.addAttribute("timeStamp", cr.getTimeStamp().toString());
456                        boolean first = true;
457                        for (Course course : cr.getCourses()) {
458                            if (first)
459                                requestEl.addAttribute("course", getId("course", course.getId()));
460                            else
461                                requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId()));
462                            first = false;
463                        }
464                        for (Choice choice : cr.getWaitlistedChoices()) {
465                            Element choiceEl = requestEl.addElement("waitlisted");
466                            choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId()));
467                            choiceEl.setText(choice.getId());
468                        }
469                        for (Choice choice : cr.getSelectedChoices()) {
470                            Element choiceEl = requestEl.addElement("selected");
471                            choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId()));
472                            choiceEl.setText(choice.getId());
473                        }
474                        if (iSaveInitial && request.getInitialAssignment() != null) {
475                            Element assignmentEl = requestEl.addElement("initial");
476                            Enrollment enrollment = request.getInitialAssignment();
477                            if (enrollment.getReservation() != null)
478                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
479                            for (Section section : enrollment.getSections()) {
480                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
481                                        getId("section", section.getId()));
482                                if (iShowNames)
483                                    sectionEl.setText(section.getName()
484                                            + " "
485                                            + (section.getTime() == null ? " Arr Hrs" : " "
486                                                    + section.getTime().getLongName())
487                                            + (section.getNrRooms() == 0 ? "" : " "
488                                                    + section.getPlacement().getRoomName(","))
489                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
490                                                    + section.getChoice().getInstructorNames()));
491                            }
492                        }
493                        if (iSaveCurrent && request.getAssignment() != null) {
494                            Element assignmentEl = requestEl.addElement("current");
495                            Enrollment enrollment = request.getAssignment();
496                            if (enrollment.getReservation() != null)
497                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
498                            for (Section section : enrollment.getSections()) {
499                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
500                                        getId("section", section.getId()));
501                                if (iShowNames)
502                                    sectionEl.setText(section.getName()
503                                            + " "
504                                            + (section.getTime() == null ? " Arr Hrs" : " "
505                                                    + section.getTime().getLongName())
506                                            + (section.getNrRooms() == 0 ? "" : " "
507                                                    + section.getPlacement().getRoomName(","))
508                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
509                                                    + section.getChoice().getInstructorNames()));
510                            }
511                        }
512                        if (iSaveBest && request.getBestAssignment() != null) {
513                            Element assignmentEl = requestEl.addElement("best");
514                            Enrollment enrollment = request.getBestAssignment();
515                            if (enrollment.getReservation() != null)
516                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
517                            for (Section section : enrollment.getSections()) {
518                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
519                                        getId("section", section.getId()));
520                                if (iShowNames)
521                                    sectionEl.setText(section.getName()
522                                            + " "
523                                            + (section.getTime() == null ? " Arr Hrs" : " "
524                                                    + section.getTime().getLongName())
525                                            + (section.getNrRooms() == 0 ? "" : " "
526                                                    + section.getPlacement().getRoomName(","))
527                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
528                                                    + section.getChoice().getInstructorNames()));
529                            }
530                        }
531                    }
532                }
533            }
534            
535            Element constrainstEl = root.addElement("constraints");
536            for (LinkedSections linkedSections: getModel().getLinkedSections()) {
537                Element linkEl = constrainstEl.addElement("linked-sections");
538                for (Offering offering: linkedSections.getOfferings())
539                    for (Subpart subpart: linkedSections.getSubparts(offering))
540                        for (Section section: linkedSections.getSections(subpart))
541                            linkEl.addElement("section")
542                                .addAttribute("offering", getId("offering", offering.getId()))
543                                .addAttribute("id", getId("section", section.getId()));
544            }
545            
546            if (getModel().getDistanceConflict() != null) {
547                Map<Long, Map<Long, Integer>> travelTimes = getModel().getDistanceConflict().getDistanceMetric().getTravelTimes();
548                if (travelTimes != null) {
549                    Element travelTimesEl = root.addElement("travel-times");
550                    for (Map.Entry<Long, Map<Long, Integer>> e1: travelTimes.entrySet())
551                        for (Map.Entry<Long, Integer> e2: e1.getValue().entrySet())
552                            travelTimesEl.addElement("travel-time")
553                                .addAttribute("id1", getId("room", e1.getKey().toString()))
554                                .addAttribute("id2", getId("room", e2.getKey().toString()))
555                                .addAttribute("minutes", e2.getValue().toString());
556                }
557            }
558    
559    
560            if (iShowNames) {
561                Progress.getInstance(getModel()).save(root);
562            }
563    
564            FileOutputStream fos = null;
565            try {
566                fos = new FileOutputStream(outFile);
567                (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(document);
568                fos.flush();
569                fos.close();
570                fos = null;
571            } finally {
572                try {
573                    if (fos != null)
574                        fos.close();
575                } catch (IOException e) {
576                }
577            }
578    
579            if (iConvertIds)
580                IdConvertor.getInstance().save();
581        }
582    
583    }