001    package net.sf.cpsolver.coursett;
002    
003    import java.io.File;
004    import java.io.FileOutputStream;
005    import java.io.IOException;
006    import java.text.DecimalFormat;
007    import java.util.BitSet;
008    import java.util.Collections;
009    import java.util.Date;
010    import java.util.Dictionary;
011    import java.util.Enumeration;
012    import java.util.HashSet;
013    import java.util.Hashtable;
014    import java.util.Iterator;
015    import java.util.Map;
016    import java.util.Set;
017    import java.util.Vector;
018    
019    import org.dom4j.Document;
020    import org.dom4j.DocumentHelper;
021    import org.dom4j.Element;
022    import org.dom4j.io.OutputFormat;
023    import org.dom4j.io.XMLWriter;
024    
025    import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint;
026    import net.sf.cpsolver.coursett.constraint.DiscouragedRoomConstraint;
027    import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
028    import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedGroupsOfTime;
029    import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedRoomsConstraint;
030    import net.sf.cpsolver.coursett.constraint.RoomConstraint;
031    import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
032    import net.sf.cpsolver.coursett.model.Lecture;
033    import net.sf.cpsolver.coursett.model.Placement;
034    import net.sf.cpsolver.coursett.model.RoomLocation;
035    import net.sf.cpsolver.coursett.model.RoomSharingModel;
036    import net.sf.cpsolver.coursett.model.Student;
037    import net.sf.cpsolver.coursett.model.TimeLocation;
038    import net.sf.cpsolver.ifs.model.Constraint;
039    import net.sf.cpsolver.ifs.solver.Solver;
040    import net.sf.cpsolver.ifs.util.Progress;
041    import net.sf.cpsolver.ifs.util.ToolBox;
042    
043    
044    /**
045     * This class saves the resultant solution in the XML format.
046     * <br><br>
047     * Parameters:
048     * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
049     * <tr><td>General.Output</td><td>{@link String}</td><td>Folder with the output solution in XML format (solution.xml)</td></tr>
050     * <tr><td>Xml.ConvertIds</td><td>{@link Boolean}</td><td>If true, ids are converted (to be able to make input data public)</td></tr>
051     * <tr><td>Xml.ShowNames</td><td>{@link Boolean}</td><td>If false, names are not exported (to be able to make input data public)</td></tr>
052     * <tr><td>Xml.SaveBest</td><td>{@link Boolean}</td><td>If true, best solution is saved.</td></tr>
053     * <tr><td>Xml.SaveInitial</td><td>{@link Boolean}</td><td>If true, initial solution is saved.</td></tr>
054     * <tr><td>Xml.SaveCurrent</td><td>{@link Boolean}</td><td>If true, current solution is saved.</td></tr>
055     * <tr><td>Xml.ExportStudentSectioning</td><td>{@link Boolean}</td><td>If true, student sectioning is saved even when there is no solution.</td></tr>
056     * </table>
057     *
058     * @version
059     * CourseTT 1.1 (University Course Timetabling)<br>
060     * Copyright (C) 2006 Tomáš Müller<br>
061     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
062     * Lazenska 391, 76314 Zlin, Czech Republic<br>
063     * <br>
064     * This library is free software; you can redistribute it and/or
065     * modify it under the terms of the GNU Lesser General Public
066     * License as published by the Free Software Foundation; either
067     * version 2.1 of the License, or (at your option) any later version.
068     * <br><br>
069     * This library is distributed in the hope that it will be useful,
070     * but WITHOUT ANY WARRANTY; without even the implied warranty of
071     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
072     * Lesser General Public License for more details.
073     * <br><br>
074     * You should have received a copy of the GNU Lesser General Public
075     * License along with this library; if not, write to the Free Software
076     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
077     */
078    
079    public class TimetableXMLSaver extends TimetableSaver {
080        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableXMLSaver.class);
081        private static DecimalFormat[] sDF = {new DecimalFormat(""),new DecimalFormat("0"),new DecimalFormat("00"),new DecimalFormat("000"),new DecimalFormat("0000"),new DecimalFormat("00000"),new DecimalFormat("000000"),new DecimalFormat("0000000")};
082        private static DecimalFormat sStudentWeightFormat = new DecimalFormat("0.0000");
083        public static boolean ANONYMISE = false;
084        
085        private boolean iConvertIds = false;
086        private boolean iShowNames = false;
087        private File iOutputFolder = null;
088        private File iOutFile = null;
089        private boolean iSaveBest = false;
090        private boolean iSaveInitial = false;
091        private boolean iSaveCurrent = false;
092        private boolean iExportStudentSectioning = false;
093        
094        private IdConvertor iIdConvertor = null;
095        
096        public TimetableXMLSaver(Solver solver) {
097            super(solver);
098            iOutputFolder = new File(getModel().getProperties().getProperty("General.Output","."+File.separator+"output"));
099            iShowNames = getModel().getProperties().getPropertyBoolean("Xml.ShowNames",false);
100            iExportStudentSectioning = getModel().getProperties().getPropertyBoolean("Xml.ExportStudentSectioning", false);
101            if (ANONYMISE) {
102                    //anonymise saved XML file -- if not set otherwise in the configuration
103                    iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds",true);
104                    iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", false);
105                    iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", false);
106                    iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", true);
107            } else {
108                    // normal operation -- if not set otherwise in the configuration
109                    iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds",false);
110                    iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", true);
111                    iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", true);
112                    iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", true);
113            }
114        }
115        
116        private String getId(String type, String id) {
117            if (!iConvertIds) return id.toString();
118            if (iIdConvertor==null) iIdConvertor = new IdConvertor(getModel().getProperties().getProperty("Xml.IdConv"));
119            return iIdConvertor.convert(type, id);
120        }
121        
122        private String getId(String type, Number id) {
123            return getId(type, id.toString());
124        }
125        
126        private String getAvailableString(boolean[] availableArray) {
127            if (availableArray==null) return null;
128            StringBuffer sb = new StringBuffer();
129            for (int i=0;i<availableArray.length;i++)
130                    sb.append(availableArray[i]?"1":"0");
131            return sb.toString();
132        }
133        
134        private static String bitset2string(BitSet b) {
135            StringBuffer sb = new StringBuffer();
136            for (int i=0;i<b.length();i++)
137                    sb.append(b.get(i)?"1":"0");
138            return sb.toString();
139        }
140    
141        public void save() throws Exception {
142            save(null);
143        }
144        
145        public void save(File outFile) throws Exception {
146            if (outFile==null)
147                    outFile = new File(iOutputFolder,"solution.xml");
148            outFile.getParentFile().mkdirs();
149            sLogger.debug("Writting XML data to:"+outFile);
150            
151            Document document = DocumentHelper.createDocument();
152            document.addComment("University Course Timetabling");
153            
154            if (iSaveCurrent && !getModel().assignedVariables().isEmpty()) {
155                StringBuffer comments = new StringBuffer("Solution Info:\n");
156                Dictionary solutionInfo=(getSolution()==null?getModel().getInfo():getSolution().getInfo());
157                for (Enumeration e=ToolBox.sortEnumeration(solutionInfo.keys());e.hasMoreElements();) {
158                    String key = (String)e.nextElement();
159                    Object value = solutionInfo.get(key);
160                    comments.append("    "+key+": "+value+"\n");
161                }
162                document.addComment(comments.toString());
163            }
164            
165            Element root = document.addElement("timetable");
166            root.addAttribute("version","2.4");
167            root.addAttribute("initiative", getModel().getProperties().getProperty("Data.Initiative"));
168            root.addAttribute("term", getModel().getProperties().getProperty("Data.Term"));
169            root.addAttribute("year", String.valueOf(getModel().getYear()));
170            root.addAttribute("created", String.valueOf(new Date()));
171            root.addAttribute("nrDays", String.valueOf(Constants.DAY_CODES.length));
172            root.addAttribute("slotsPerDay", String.valueOf(Constants.SLOTS_PER_DAY));
173            if (!iConvertIds && getModel().getProperties().getProperty("General.SessionId")!=null)
174                    root.addAttribute("session", getModel().getProperties().getProperty("General.SessionId"));
175            if (iShowNames && !iConvertIds && getModel().getProperties().getProperty("General.SolverGroupId")!=null)
176                    root.addAttribute("solverGroup", getId("solverGroup",getModel().getProperties().getProperty("General.SolverGroupId")));
177            
178            Hashtable roomElements = new Hashtable();
179            
180            Element roomsEl = root.addElement("rooms");
181            for (Enumeration e=getModel().getRoomConstraints().elements();e.hasMoreElements();) {
182                    RoomConstraint roomConstraint = (RoomConstraint)e.nextElement();
183                    Element roomEl = roomsEl.addElement("room").addAttribute("id", getId("room", roomConstraint.getResourceId()));
184                    roomEl.addAttribute("constraint", "true");
185                    if (roomConstraint instanceof DiscouragedRoomConstraint)
186                            roomEl.addAttribute("discouraged", "true");
187                    if (iShowNames) {
188                            roomEl.addAttribute("name",roomConstraint.getRoomName());
189                    }
190                    if (!iConvertIds && roomConstraint.getBuildingId()!=null)
191                            roomEl.addAttribute("building", getId("bldg", roomConstraint.getBuildingId()));
192                    roomElements.put(getId("room", roomConstraint.getResourceId()), roomEl);
193                roomEl.addAttribute("capacity", String.valueOf(roomConstraint.getCapacity()));
194                if (roomConstraint.getPosX()>0 || roomConstraint.getPosY()>0)
195                    roomEl.addAttribute("location", roomConstraint.getPosX()+","+roomConstraint.getPosY());
196                if (roomConstraint.getIgnoreTooFar())
197                    roomEl.addAttribute("ignoreTooFar", "true");
198                if (!roomConstraint.getConstraint())
199                    roomEl.addAttribute("fake", "true");
200                if (roomConstraint.getSharingModel()!=null) {
201                    RoomSharingModel sharingModel = roomConstraint.getSharingModel(); 
202                    Element sharingEl = roomEl.addElement("sharing");
203                    sharingEl.addElement("pattern").addAttribute("unit", "6").setText(sharingModel.getPreferences());
204                    sharingEl.addElement("freeForAll").addAttribute("value",String.valueOf(RoomSharingModel.sFreeForAllPrefChar));
205                    sharingEl.addElement("notAvailable").addAttribute("value",String.valueOf(RoomSharingModel.sNotAvailablePrefChar));
206                    for (int i=0;i<sharingModel.getNrDepartments();i++) {
207                            sharingEl.addElement("department").addAttribute("value", String.valueOf((char)('0'+i))).addAttribute("id", getId("dept", sharingModel.getDepartmentIds()[i]));
208                    }
209                }
210                if (roomConstraint.getType()!=null && iShowNames)
211                    roomEl.addAttribute("type",roomConstraint.getType().toString());
212            }
213            
214            Element instructorsEl = root.addElement("instructors");
215    
216            Element departmentsEl = root.addElement("departments");
217            Hashtable depts = new Hashtable();
218            
219            Element configsEl = (iShowNames?root.addElement("configurations"):null);
220            HashSet configs = new HashSet();
221            
222            Element classesEl = root.addElement("classes");
223            Hashtable classElements = new Hashtable();
224            Vector vars = new Vector(getModel().variables());
225            if (getModel().hasConstantVariables()) vars.addAll(getModel().constantVariables());
226            for (Enumeration e1=vars.elements();e1.hasMoreElements();) {
227                Lecture lecture = (Lecture)e1.nextElement();
228                Placement placement = (Placement)lecture.getAssignment();
229                if (lecture.isCommitted() && placement==null)
230                    placement = (Placement)lecture.getInitialAssignment();
231                Placement initialPlacement = (Placement)lecture.getInitialAssignment();
232                //if (initialPlacement==null) initialPlacement = (Placement)lecture.getAssignment();
233                Placement bestPlacement = (Placement)lecture.getBestAssignment();
234                Element classEl = classesEl.addElement("class").addAttribute("id", getId("class", lecture.getClassId()));
235                classElements.put(lecture.getClassId(), classEl);
236                if (iShowNames && lecture.getNote()!=null)
237                    classEl.addAttribute("note",lecture.getNote());
238                if (iShowNames && !lecture.isCommitted()) classEl.addAttribute("ord",String.valueOf(lecture.getOrd()));
239                if (iShowNames && lecture.getSolverGroupId()!=null)
240                    classEl.addAttribute("solverGroup", getId("solverGroup",lecture.getSolverGroupId()));
241                if (lecture.getParent()==null && lecture.getConfiguration()!=null) {
242                    if (!iShowNames)
243                        classEl.addAttribute("offering", getId("offering", lecture.getConfiguration().getOfferingId().toString()));
244                    classEl.addAttribute("config",  getId("config", lecture.getConfiguration().getConfigId().toString()));
245                    if (iShowNames && configs.add(lecture.getConfiguration())) {
246                            configsEl.addElement("config").
247                                    addAttribute("id", getId("config", lecture.getConfiguration().getConfigId().toString())).
248                                    addAttribute("limit", String.valueOf(lecture.getConfiguration().getLimit())).
249                                    addAttribute("offering", getId("offering", lecture.getConfiguration().getOfferingId().toString()));
250                    }
251                }
252                classEl.addAttribute("committed", (lecture.isCommitted()?"true":"false"));
253                if (lecture.getParent()!=null)
254                    classEl.addAttribute("parent", getId("class", lecture.getParent().getClassId()));
255                if (lecture.getSchedulingSubpartId()!=null)
256                    classEl.addAttribute("subpart", getId("subpart", lecture.getSchedulingSubpartId()));
257                if (iShowNames && lecture.isCommitted() && placement!=null && placement.getAssignmentId()!=null) {
258                    classEl.addAttribute("assignment", getId("assignment", placement.getAssignmentId()));
259                }
260                if (!lecture.isCommitted()) {
261                    if (lecture.minClassLimit()==lecture.maxClassLimit()) {
262                            classEl.addAttribute("classLimit", String.valueOf(lecture.maxClassLimit()));
263                    } else {
264                            classEl.addAttribute("minClassLimit", String.valueOf(lecture.minClassLimit()));
265                            classEl.addAttribute("maxClassLimit", String.valueOf(lecture.maxClassLimit()));
266                    }
267                    if (lecture.roomToLimitRatio()!=1.0)
268                            classEl.addAttribute("roomToLimitRatio", String.valueOf(lecture.roomToLimitRatio()));
269                }
270                if (lecture.getNrRooms()!=1)
271                    classEl.addAttribute("nrRooms",String.valueOf(lecture.getNrRooms()));
272                if (iShowNames)
273                    classEl.addAttribute("name", lecture.getName());
274                if (lecture.getDeptSpreadConstraint()!=null) {
275                    classEl.addAttribute("department", getId("dept", lecture.getDeptSpreadConstraint().getDepartmentId()));
276                    depts.put(lecture.getDeptSpreadConstraint().getDepartmentId(),lecture.getDeptSpreadConstraint().getName());
277                }
278                if (lecture.getScheduler()!=null)
279                    classEl.addAttribute("scheduler", getId("dept", lecture.getScheduler()));
280                for (Enumeration e2=lecture.getInstructorConstraints().elements();e2.hasMoreElements();) {
281                    InstructorConstraint ic = (InstructorConstraint)e2.nextElement();
282                    Element instrEl = classEl.addElement("instructor").addAttribute("id", getId("inst", ic.getResourceId()));
283                    if ((lecture.isCommitted() || iSaveCurrent) && placement!=null) instrEl.addAttribute("solution","true");
284                    if (iSaveInitial && initialPlacement!=null) instrEl.addAttribute("initial","true");
285                    if (iSaveBest && bestPlacement!=null && !bestPlacement.equals(placement)) instrEl.addAttribute("best", "true");
286                }
287                for (Enumeration e2=lecture.roomLocations().elements();e2.hasMoreElements();) {
288                    RoomLocation rl = (RoomLocation)e2.nextElement();
289                    Element roomLocationEl = (Element)classEl.addElement("room");
290                    roomLocationEl.addAttribute("id",  getId("room", rl.getId()));
291                    roomLocationEl.addAttribute("pref", String.valueOf(rl.getPreference()));
292                    if ((lecture.isCommitted() || iSaveCurrent) && placement!=null && placement.hasRoomLocation(rl.getId())) roomLocationEl.addAttribute("solution", "true");
293                    if (iSaveInitial && initialPlacement!=null && initialPlacement.hasRoomLocation(rl.getId())) roomLocationEl.addAttribute("initial", "true");
294                    if (iSaveBest && bestPlacement!=null && !bestPlacement.equals(placement) && bestPlacement.hasRoomLocation(rl.getId())) roomLocationEl.addAttribute("best", "true");
295                    if (!roomElements.containsKey(getId("room", rl.getId()))) {
296                            //room location without room constraint
297                            Element roomEl = roomsEl.addElement("room").addAttribute("id", getId("room", rl.getId()));
298                            roomEl.addAttribute("constraint", "false");
299                            if (!iConvertIds && rl.getBuildingId()!=null)
300                                    roomEl.addAttribute("building", getId("bldg", rl.getBuildingId()));
301                            if (iShowNames) {
302                                    roomEl.addAttribute("name",rl.getName());
303                            }
304                            roomElements.put(getId("room", rl.getId()), roomEl);
305                        roomEl.addAttribute("capacity", String.valueOf(rl.getRoomSize()));
306                        if (rl.getPosX()>0 || rl.getPosY()>0)
307                            roomEl.addAttribute("location", rl.getPosX()+","+rl.getPosY());
308                        if (rl.getIgnoreTooFar())
309                            roomEl.addAttribute("ignoreTooFar", "true");
310                    }
311                }
312                boolean first = true;
313                for (Enumeration e2=lecture.timeLocations().elements();e2.hasMoreElements();) {
314                    TimeLocation tl = (TimeLocation)e2.nextElement();
315                    Element timeLocationEl = (Element)classEl.addElement("time");
316                    timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))));
317                    timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
318                    timeLocationEl.addAttribute("length", String.valueOf(tl.getLength()));
319                    if (iShowNames) {
320                        timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
321                            timeLocationEl.addAttribute("pref", String.valueOf((int)tl.getPreference()));
322                            timeLocationEl.addAttribute("npref", String.valueOf(tl.getNormalizedPreference()));
323                    } else {
324                            timeLocationEl.addAttribute("pref", String.valueOf(tl.getNormalizedPreference()));
325                    }
326                    if (!iConvertIds && tl.getTimePatternId()!=null)
327                            timeLocationEl.addAttribute("pattern", getId("pat",tl.getTimePatternId()));
328                    if (first) {
329                        if (!iConvertIds && tl.getDatePatternId()!=null)
330                            classEl.addAttribute("datePattern", getId("dpat",String.valueOf(tl.getDatePatternId())));
331                        if (iShowNames)
332                            classEl.addAttribute("datePatternName", tl.getDatePatternName());
333                        classEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
334                            first = false;
335                    }
336                    if ((lecture.isCommitted() || iSaveCurrent) && placement!=null && placement.getTimeLocation().equals(tl)) timeLocationEl.addAttribute("solution", "true");
337                    if (iSaveInitial && initialPlacement!=null && initialPlacement.getTimeLocation().equals(tl)) timeLocationEl.addAttribute("initial", "true");
338                    if (iSaveBest && bestPlacement!=null && !bestPlacement.equals(placement) && bestPlacement.getTimeLocation().equals(tl)) timeLocationEl.addAttribute("best", "true");
339                }
340            }
341            
342            for (Enumeration e=getModel().getInstructorConstraints().elements();e.hasMoreElements();) {
343                    InstructorConstraint ic = (InstructorConstraint)e.nextElement();
344                if (iShowNames || ic.isIgnoreDistances()) {
345                    Element instrEl = instructorsEl.addElement("instructor").addAttribute("id", getId("inst", ic.getResourceId()));
346                    if (iShowNames) {
347                        if (ic.getPuid()!=null && ic.getPuid().length()>0)
348                            instrEl.addAttribute("puid",ic.getPuid());
349                        instrEl.addAttribute("name",ic.getName());
350                        if (ic.getType()!=null && iShowNames)
351                            instrEl.addAttribute("type",ic.getType().toString());
352                    }
353                    if (ic.isIgnoreDistances()) {
354                        instrEl.addAttribute("ignDist","true");
355                    }
356                }
357                if (ic.getAvailableArray()!=null) {
358                    HashSet done = new HashSet();
359                    for (int i=0;i<ic.getAvailableArray().length;i++) {
360                        if (ic.getAvailableArray()[i]!=null) {
361                            for (Enumeration f=ic.getAvailableArray()[i].elements();f.hasMoreElements();) {
362                                Placement placement = (Placement)f.nextElement();
363                                Lecture lecture = (Lecture)placement.variable();
364                                if (done.add(lecture.getClassId())) {
365                                    Element classEl = (Element)classElements.get(lecture.getClassId());
366                                    classEl.addElement("instructor").addAttribute("id", getId("inst", ic.getResourceId())).addAttribute("solution","true");
367                                }
368                            }
369                        }
370                    }
371                }
372            }
373            if (instructorsEl.elements().isEmpty())
374                    root.remove(instructorsEl);
375            
376            
377            Element grConstraintsEl = root.addElement("groupConstraints");
378            Hashtable grConv = new Hashtable();
379            for (Enumeration e1=getModel().getGroupConstraints().elements();e1.hasMoreElements();) {
380                net.sf.cpsolver.coursett.constraint.GroupConstraint gc = (net.sf.cpsolver.coursett.constraint.GroupConstraint)e1.nextElement();
381                Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId("gr", String.valueOf(gc.getId())));
382                grEl.addAttribute("type", gc.getType());
383                grEl.addAttribute("pref", gc.getPrologPreference());
384                for (Enumeration e2=gc.variables().elements(); e2.hasMoreElements();) {
385                    Lecture l = (Lecture)e2.nextElement();
386                    grEl.addElement("class").addAttribute("id",getId("class", l.getClassId()));
387                }
388            }
389            for (Enumeration e1=getModel().getSpreadConstraints().elements();e1.hasMoreElements();) {
390                SpreadConstraint spread = (SpreadConstraint)e1.nextElement();
391                Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId("gr", String.valueOf(spread.getId())));
392                grEl.addAttribute("type", "SPREAD");
393                grEl.addAttribute("pref", Constants.sPreferenceRequired);
394                if (iShowNames)
395                    grEl.addAttribute("name", spread.getName());
396                for (Enumeration e2=spread.variables().elements(); e2.hasMoreElements();) {
397                    Lecture l = (Lecture)e2.nextElement();
398                    grEl.addElement("class").addAttribute("id",getId("class", l.getClassId()));
399                }
400            }
401            for (Enumeration e1=getModel().constraints().elements();e1.hasMoreElements();) {
402                    Constraint c = (Constraint)e1.nextElement();
403                    if (c instanceof MinimizeNumberOfUsedRoomsConstraint) {
404                    Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId("gr", String.valueOf(c.getId())));
405                    grEl.addAttribute("type", "MIN_ROOM_USE");
406                    grEl.addAttribute("pref", Constants.sPreferenceRequired);
407                    for (Enumeration e2=c.variables().elements(); e2.hasMoreElements();) {
408                        Lecture l = (Lecture)e2.nextElement();
409                        grEl.addElement("class").addAttribute("id",getId("class", l.getClassId()));
410                    }
411                    }
412                    if (c instanceof MinimizeNumberOfUsedGroupsOfTime) {
413                    Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId("gr", String.valueOf(c.getId())));
414                    grEl.addAttribute("type", ((MinimizeNumberOfUsedGroupsOfTime)c).getConstraintName());
415                    grEl.addAttribute("pref", Constants.sPreferenceRequired);
416                    for (Enumeration e2=c.variables().elements(); e2.hasMoreElements();) {
417                        Lecture l = (Lecture)e2.nextElement();
418                        grEl.addElement("class").addAttribute("id",getId("class", l.getClassId()));
419                    }
420                    }
421            }
422            for (Enumeration e1=getModel().getClassLimitConstraints().elements();e1.hasMoreElements();) {
423                    ClassLimitConstraint clc = (ClassLimitConstraint)e1.nextElement();
424                Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId("gr", String.valueOf(clc.getId())));
425                grEl.addAttribute("type", "CLASS_LIMIT");
426                grEl.addAttribute("pref", Constants.sPreferenceRequired);
427                if (clc.getParentLecture()!=null) {
428                    grEl.addElement("parentClass").addAttribute("id",getId("class", clc.getParentLecture().getClassId()));
429                } else 
430                    grEl.addAttribute("courseLimit",String.valueOf(clc.classLimit()-clc.getClassLimitDelta()));
431                    if (clc.getClassLimitDelta()!=0)
432                            grEl.addAttribute("delta",String.valueOf(clc.getClassLimitDelta()));
433                if (iShowNames)
434                    grEl.addAttribute("name", clc.getName());
435                for (Enumeration e2=clc.variables().elements(); e2.hasMoreElements();) {
436                    Lecture l = (Lecture)e2.nextElement();
437                    grEl.addElement("class").addAttribute("id",getId("class", l.getClassId()));
438                }
439            }
440            
441            Hashtable students = new Hashtable();
442            for (Enumeration e1=getModel().variables().elements();e1.hasMoreElements();) {
443                Lecture lecture = (Lecture)e1.nextElement();
444                for (Iterator i2=lecture.students().iterator();i2.hasNext();) {
445                    Student student = (Student)i2.next();
446                    Vector enrls = (Vector)students.get(student);
447                    if (enrls==null) {
448                        enrls = new Vector();
449                        students.put(student, enrls);
450                    }
451                    enrls.add(getId("class", lecture.getClassId()));
452                }
453            }
454            
455            Element studentsEl = root.addElement("students");
456            Hashtable studentConv = new Hashtable();
457            for (Enumeration e1=ToolBox.sortEnumeration(students.keys());e1.hasMoreElements();) {
458                Student student = (Student)e1.nextElement();
459                Element stEl = studentsEl.addElement("student").addAttribute("id", getId("student", student.getId()));
460                for (Iterator i=student.getOfferingsMap().entrySet().iterator();i.hasNext();) {
461                    Map.Entry entry = (Map.Entry)i.next();
462                    Long offeringId = (Long)entry.getKey();
463                    Double weight = (Double)entry.getValue();
464                    Element offEl = stEl.addElement("offering").addAttribute("id", getId("offering", offeringId.toString()));
465                    if (weight.doubleValue()!=1.0)
466                        offEl.addAttribute("weight", sStudentWeightFormat.format(weight));
467                }
468                if (iExportStudentSectioning || getModel().unassignedVariables().isEmpty() || student.getOfferingsMap().isEmpty()) {
469                    Vector lectures = (Vector)students.get(student);
470                    Collections.sort(lectures);
471                    for (Enumeration e2=lectures.elements();e2.hasMoreElements();) {
472                            stEl.addElement("class").addAttribute("id", (String)e2.nextElement());
473                    }
474                }
475                Hashtable canNotEnroll = student.canNotEnrollSections();
476                if (canNotEnroll!=null) {
477                    for (Enumeration e2=canNotEnroll.elements();e2.hasMoreElements();) {
478                            Set canNotEnrollLects = (Set)e2.nextElement();
479                        for (Iterator i3=canNotEnrollLects.iterator();i3.hasNext();) {
480                            stEl.addElement("prohibited-class").addAttribute("id", getId("class",((Lecture)i3.next()).getClassId()));
481                        }
482                    }
483                }
484                
485                if (student.getCommitedPlacements()!=null) {
486                    for (Iterator i2=student.getCommitedPlacements().iterator();i2.hasNext();) {
487                            Placement placement = (Placement)i2.next();
488                            Lecture lecture = (Lecture)placement.variable();
489                            stEl.addElement("class").addAttribute("id",getId("class", lecture.getClassId()));
490                    }
491                }
492            }
493            
494            if (getModel().getProperties().getPropertyInt("MPP.GenTimePert", 0)>0) {
495                Element perturbationsEl = root.addElement("perturbations");
496                int nrChanges = getModel().getProperties().getPropertyInt("MPP.GenTimePert", 0);
497                Vector lectures = new Vector();
498                while (lectures.size()<nrChanges) {
499                    Lecture lecture = (Lecture)ToolBox.random(getModel().assignedVariables());
500                    if (lecture.isCommitted() || lecture.timeLocations().size()<=1 || lectures.contains(lecture)) continue;
501                    Placement placement = (Placement)lecture.getAssignment();
502                    TimeLocation tl = (TimeLocation)placement.getTimeLocation();
503                    perturbationsEl.addElement("class")
504                        .addAttribute("id", getId("class", lecture.getClassId()))
505                        .addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))))
506                        .addAttribute("start", String.valueOf(tl.getStartSlot()))
507                        .addAttribute("length", String.valueOf(tl.getLength()));
508                    lectures.add(lecture);
509                }
510            }
511            
512            for (Iterator i=depts.entrySet().iterator();i.hasNext();) {
513                    Map.Entry entry = (Map.Entry)i.next();
514                    Object id = entry.getKey();
515                    String name = (String)entry.getValue();
516                    if (iShowNames) {
517                            departmentsEl.addElement("department").addAttribute("id",getId("dept",id.toString())).addAttribute("name",name);
518                    }
519            }
520            if (departmentsEl.elements().isEmpty())
521                    root.remove(departmentsEl);
522            
523            if (iShowNames) {
524                    Progress.getInstance(getModel()).save(root);
525            
526                try {
527                    getSolver().getClass().getMethod("save", new Class[] {Element.class}).invoke(getSolver(), new Object[] {root});
528                } catch (Exception e) {}
529            }
530                            
531            FileOutputStream fos = null;
532            try {
533                    fos = new FileOutputStream(outFile);
534                    (new XMLWriter(fos,OutputFormat.createPrettyPrint())).write(document);
535                    fos.flush();fos.close();fos=null;
536            } finally {
537                    try {
538                            if (fos!=null) fos.close();
539                    } catch (IOException e) {}
540            }
541            
542            if (iConvertIds) iIdConvertor.save();
543        }
544    }