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