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                        }
325                    }
326                }
327                if (!offering.getReservations().isEmpty()) {
328                    for (Reservation r: offering.getReservations()) {
329                        Element reservationEl = offeringEl.addElement("reservation");
330                        reservationEl.addAttribute("id", getId("reservation", r.getId()));
331                        reservationEl.addAttribute("expired", r.isExpired() ? "true" : "false");
332                        if (r instanceof GroupReservation) {
333                            GroupReservation gr = (GroupReservation)r;
334                            reservationEl.addAttribute("type", "group");
335                            for (Long studentId: gr.getStudentIds())
336                                reservationEl.addElement("student").addAttribute("id", getId("student", studentId));
337                            if (gr.getReservationLimit() >= 0.0)
338                                reservationEl.addAttribute("limit", String.valueOf(gr.getReservationLimit()));
339                        } else if (r instanceof IndividualReservation) {
340                            reservationEl.addAttribute("type", "individual");
341                            for (Long studentId: ((IndividualReservation)r).getStudentIds())
342                                reservationEl.addElement("student").addAttribute("id", getId("student", studentId));
343                        } else if (r instanceof CurriculumReservation) {
344                            reservationEl.addAttribute("type", "curriculum");
345                            CurriculumReservation cr = (CurriculumReservation)r;
346                            if (cr.getReservationLimit() >= 0.0)
347                                reservationEl.addAttribute("limit", String.valueOf(cr.getReservationLimit()));
348                            reservationEl.addAttribute("area", cr.getAcademicArea());
349                            for (String clasf: cr.getClassifications())
350                                reservationEl.addElement("classification").addAttribute("code", clasf);
351                            for (String major: cr.getMajors())
352                                reservationEl.addElement("major").addAttribute("code", major);
353                        } else if (r instanceof CourseReservation) {
354                            reservationEl.addAttribute("type", "course");
355                            CourseReservation cr = (CourseReservation)r;
356                            reservationEl.addAttribute("course", getId("course",cr.getCourse().getId()));
357                        } else if (r instanceof DummyReservation) {
358                            reservationEl.addAttribute("type", "dummy");
359                        }
360                        for (Config config: r.getConfigs())
361                            reservationEl.addElement("config").addAttribute("id", getId("config", config.getId()));
362                        for (Map.Entry<Subpart, Set<Section>> entry: r.getSections().entrySet()) {
363                            for (Section section: entry.getValue()) {
364                                reservationEl.addElement("section").addAttribute("id", getId("section", section.getId()));
365                            }
366                        }
367                    }
368                }
369            }
370    
371            Element studentsEl = root.addElement("students");
372            for (Student student : getModel().getStudents()) {
373                Element studentEl = studentsEl.addElement("student");
374                studentEl.addAttribute("id", getId("student", student.getId()));
375                if (iShowNames) {
376                    if (student.getExternalId() != null && !student.getExternalId().isEmpty())
377                        studentEl.addAttribute("externalId", student.getExternalId());
378                    if (student.getName() != null && !student.getName().isEmpty())
379                        studentEl.addAttribute("name", student.getName());
380                    if (student.getStatus() != null && !student.getStatus().isEmpty())
381                        studentEl.addAttribute("status", student.getStatus());
382                }
383                if (student.isDummy())
384                    studentEl.addAttribute("dummy", "true");
385                if (iSaveStudentInfo) {
386                    for (AcademicAreaCode aac : student.getAcademicAreaClasiffications()) {
387                        Element aacEl = studentEl.addElement("classification");
388                        if (aac.getArea() != null)
389                            aacEl.addAttribute("area", aac.getArea());
390                        if (aac.getCode() != null)
391                            aacEl.addAttribute("code", aac.getCode());
392                    }
393                    for (AcademicAreaCode aac : student.getMajors()) {
394                        Element aacEl = studentEl.addElement("major");
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.getMinors()) {
401                        Element aacEl = studentEl.addElement("minor");
402                        if (aac.getArea() != null)
403                            aacEl.addAttribute("area", aac.getArea());
404                        if (aac.getCode() != null)
405                            aacEl.addAttribute("code", aac.getCode());
406                    }
407                }
408                for (Request request : student.getRequests()) {
409                    if (request instanceof FreeTimeRequest) {
410                        Element requestEl = studentEl.addElement("freeTime");
411                        FreeTimeRequest ft = (FreeTimeRequest) request;
412                        requestEl.addAttribute("id", getId("request", request.getId()));
413                        requestEl.addAttribute("priority", String.valueOf(request.getPriority()));
414                        if (request.isAlternative())
415                            requestEl.addAttribute("alternative", "true");
416                        if (request.getWeight() != 1.0)
417                            requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight()));
418                        TimeLocation tl = ft.getTime();
419                        if (tl != null) {
420                            requestEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl
421                                    .getDayCode()))));
422                            requestEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
423                            requestEl.addAttribute("length", String.valueOf(tl.getLength()));
424                            if (iShowNames && tl.getDatePatternId() != null)
425                                requestEl.addAttribute("datePattern", tl.getDatePatternId().toString());
426                            requestEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
427                            if (iShowNames)
428                                requestEl.setText(tl.getLongName());
429                        }
430                        if (iSaveInitial && request.getInitialAssignment() != null) {
431                            requestEl.addElement("initial");
432                        }
433                        if (iSaveCurrent && request.getAssignment() != null) {
434                            requestEl.addElement("current");
435                        }
436                        if (iSaveBest && request.getBestAssignment() != null) {
437                            requestEl.addElement("best");
438                        }
439                    } else if (request instanceof CourseRequest) {
440                        CourseRequest cr = (CourseRequest) request;
441                        Element requestEl = studentEl.addElement("course");
442                        requestEl.addAttribute("id", getId("request", request.getId()));
443                        requestEl.addAttribute("priority", String.valueOf(request.getPriority()));
444                        if (request.isAlternative())
445                            requestEl.addAttribute("alternative", "true");
446                        if (request.getWeight() != 1.0)
447                            requestEl.addAttribute("weight", sStudentWeightFormat.format(request.getWeight()));
448                        requestEl.addAttribute("waitlist", cr.isWaitlist() ? "true" : "false");
449                        if (cr.getTimeStamp() != null)
450                            requestEl.addAttribute("timeStamp", cr.getTimeStamp().toString());
451                        boolean first = true;
452                        for (Course course : cr.getCourses()) {
453                            if (first)
454                                requestEl.addAttribute("course", getId("course", course.getId()));
455                            else
456                                requestEl.addElement("alternative").addAttribute("course", getId("course", course.getId()));
457                            first = false;
458                        }
459                        for (Choice choice : cr.getWaitlistedChoices()) {
460                            Element choiceEl = requestEl.addElement("waitlisted");
461                            choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId()));
462                            choiceEl.setText(choice.getId());
463                        }
464                        for (Choice choice : cr.getSelectedChoices()) {
465                            Element choiceEl = requestEl.addElement("selected");
466                            choiceEl.addAttribute("offering", getId("offering", choice.getOffering().getId()));
467                            choiceEl.setText(choice.getId());
468                        }
469                        if (iSaveInitial && request.getInitialAssignment() != null) {
470                            Element assignmentEl = requestEl.addElement("initial");
471                            Enrollment enrollment = request.getInitialAssignment();
472                            if (enrollment.getReservation() != null)
473                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
474                            for (Section section : enrollment.getSections()) {
475                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
476                                        getId("section", section.getId()));
477                                if (iShowNames)
478                                    sectionEl.setText(section.getName()
479                                            + " "
480                                            + (section.getTime() == null ? " Arr Hrs" : " "
481                                                    + section.getTime().getLongName())
482                                            + (section.getNrRooms() == 0 ? "" : " "
483                                                    + section.getPlacement().getRoomName(","))
484                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
485                                                    + section.getChoice().getInstructorNames()));
486                            }
487                        }
488                        if (iSaveCurrent && request.getAssignment() != null) {
489                            Element assignmentEl = requestEl.addElement("current");
490                            Enrollment enrollment = request.getAssignment();
491                            if (enrollment.getReservation() != null)
492                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
493                            for (Section section : enrollment.getSections()) {
494                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
495                                        getId("section", section.getId()));
496                                if (iShowNames)
497                                    sectionEl.setText(section.getName()
498                                            + " "
499                                            + (section.getTime() == null ? " Arr Hrs" : " "
500                                                    + section.getTime().getLongName())
501                                            + (section.getNrRooms() == 0 ? "" : " "
502                                                    + section.getPlacement().getRoomName(","))
503                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
504                                                    + section.getChoice().getInstructorNames()));
505                            }
506                        }
507                        if (iSaveBest && request.getBestAssignment() != null) {
508                            Element assignmentEl = requestEl.addElement("best");
509                            Enrollment enrollment = request.getBestAssignment();
510                            if (enrollment.getReservation() != null)
511                                assignmentEl.addAttribute("reservation", getId("reservation", enrollment.getReservation().getId()));
512                            for (Section section : enrollment.getSections()) {
513                                Element sectionEl = assignmentEl.addElement("section").addAttribute("id",
514                                        getId("section", section.getId()));
515                                if (iShowNames)
516                                    sectionEl.setText(section.getName()
517                                            + " "
518                                            + (section.getTime() == null ? " Arr Hrs" : " "
519                                                    + section.getTime().getLongName())
520                                            + (section.getNrRooms() == 0 ? "" : " "
521                                                    + section.getPlacement().getRoomName(","))
522                                            + (section.getChoice().getInstructorNames() == null ? "" : " "
523                                                    + section.getChoice().getInstructorNames()));
524                            }
525                        }
526                    }
527                }
528            }
529            
530            Element constrainstEl = root.addElement("constraints");
531            for (LinkedSections linkedSections: getModel().getLinkedSections()) {
532                Element linkEl = constrainstEl.addElement("linked-sections");
533                for (Offering offering: linkedSections.getOfferings())
534                    for (Subpart subpart: linkedSections.getSubparts(offering))
535                        for (Section section: linkedSections.getSections(subpart))
536                            linkEl.addElement("section")
537                                .addAttribute("offering", getId("offering", offering.getId()))
538                                .addAttribute("id", getId("section", section.getId()));
539            }
540            
541            if (getModel().getDistanceConflict() != null) {
542                Map<Long, Map<Long, Integer>> travelTimes = getModel().getDistanceConflict().getDistanceMetric().getTravelTimes();
543                if (travelTimes != null) {
544                    Element travelTimesEl = root.addElement("travel-times");
545                    for (Map.Entry<Long, Map<Long, Integer>> e1: travelTimes.entrySet())
546                        for (Map.Entry<Long, Integer> e2: e1.getValue().entrySet())
547                            travelTimesEl.addElement("travel-time")
548                                .addAttribute("id1", getId("room", e1.getKey().toString()))
549                                .addAttribute("id2", getId("room", e2.getKey().toString()))
550                                .addAttribute("minutes", e2.getValue().toString());
551                }
552            }
553    
554    
555            if (iShowNames) {
556                Progress.getInstance(getModel()).save(root);
557            }
558    
559            FileOutputStream fos = null;
560            try {
561                fos = new FileOutputStream(outFile);
562                (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(document);
563                fos.flush();
564                fos.close();
565                fos = null;
566            } finally {
567                try {
568                    if (fos != null)
569                        fos.close();
570                } catch (IOException e) {
571                }
572            }
573    
574            if (iConvertIds)
575                IdConvertor.getInstance().save();
576        }
577    
578    }