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.ArrayList;
008 import java.util.BitSet;
009 import java.util.Collections;
010 import java.util.Date;
011 import java.util.HashSet;
012 import java.util.HashMap;
013 import java.util.Iterator;
014 import java.util.List;
015 import java.util.Map;
016 import java.util.Set;
017 import java.util.TreeSet;
018
019 import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint;
020 import net.sf.cpsolver.coursett.constraint.DiscouragedRoomConstraint;
021 import net.sf.cpsolver.coursett.constraint.FlexibleConstraint;
022 import net.sf.cpsolver.coursett.constraint.GroupConstraint;
023 import net.sf.cpsolver.coursett.constraint.IgnoreStudentConflictsConstraint;
024 import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
025 import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedGroupsOfTime;
026 import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedRoomsConstraint;
027 import net.sf.cpsolver.coursett.constraint.RoomConstraint;
028 import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
029 import net.sf.cpsolver.coursett.model.Configuration;
030 import net.sf.cpsolver.coursett.model.Lecture;
031 import net.sf.cpsolver.coursett.model.Placement;
032 import net.sf.cpsolver.coursett.model.RoomLocation;
033 import net.sf.cpsolver.coursett.model.RoomSharingModel;
034 import net.sf.cpsolver.coursett.model.Student;
035 import net.sf.cpsolver.coursett.model.TimeLocation;
036 import net.sf.cpsolver.ifs.model.Constraint;
037 import net.sf.cpsolver.ifs.solver.Solver;
038 import net.sf.cpsolver.ifs.util.Progress;
039 import net.sf.cpsolver.ifs.util.ToolBox;
040
041 import org.dom4j.Document;
042 import org.dom4j.DocumentHelper;
043 import org.dom4j.Element;
044 import org.dom4j.io.OutputFormat;
045 import org.dom4j.io.XMLWriter;
046
047 /**
048 * This class saves the resultant solution in the XML format. <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.ExportStudentSectioning</td>
089 * <td>{@link Boolean}</td>
090 * <td>If true, student sectioning is saved even when there is no solution.</td>
091 * </tr>
092 * </table>
093 *
094 * @version CourseTT 1.2 (University Course Timetabling)<br>
095 * Copyright (C) 2006 - 2010 Tomas Muller<br>
096 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
097 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
098 * <br>
099 * This library is free software; you can redistribute it and/or modify
100 * it under the terms of the GNU Lesser General Public License as
101 * published by the Free Software Foundation; either version 3 of the
102 * License, or (at your option) any later version. <br>
103 * <br>
104 * This library is distributed in the hope that it will be useful, but
105 * WITHOUT ANY WARRANTY; without even the implied warranty of
106 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
107 * Lesser General Public License for more details. <br>
108 * <br>
109 * You should have received a copy of the GNU Lesser General Public
110 * License along with this library; if not see
111 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
112 */
113
114 public class TimetableXMLSaver extends TimetableSaver {
115 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableXMLSaver.class);
116 private static DecimalFormat[] sDF = { new DecimalFormat(""), new DecimalFormat("0"), new DecimalFormat("00"),
117 new DecimalFormat("000"), new DecimalFormat("0000"), new DecimalFormat("00000"),
118 new DecimalFormat("000000"), new DecimalFormat("0000000") };
119 private static DecimalFormat sStudentWeightFormat = new DecimalFormat("0.0000");
120 public static boolean ANONYMISE = false;
121
122 private boolean iConvertIds = false;
123 private boolean iShowNames = false;
124 private File iOutputFolder = null;
125 private boolean iSaveBest = false;
126 private boolean iSaveInitial = false;
127 private boolean iSaveCurrent = false;
128 private boolean iExportStudentSectioning = false;
129
130 private IdConvertor iIdConvertor = null;
131
132 public TimetableXMLSaver(Solver<Lecture, Placement> solver) {
133 super(solver);
134
135
136 iOutputFolder = new File(getModel().getProperties().getProperty("General.Output",
137 "." + File.separator + "output"));
138 iShowNames = getModel().getProperties().getPropertyBoolean("Xml.ShowNames", false);
139 iExportStudentSectioning = getModel().getProperties().getPropertyBoolean("Xml.ExportStudentSectioning", false);
140 if (ANONYMISE) {
141 // anonymise saved XML file -- if not set otherwise in the
142 // configuration
143 iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds", true);
144 iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", false);
145 iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", false);
146 iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", true);
147 } else {
148 // normal operation -- if not set otherwise in the configuration
149 iConvertIds = getModel().getProperties().getPropertyBoolean("Xml.ConvertIds", false);
150 iSaveBest = getModel().getProperties().getPropertyBoolean("Xml.SaveBest", true);
151 iSaveInitial = getModel().getProperties().getPropertyBoolean("Xml.SaveInitial", true);
152 iSaveCurrent = getModel().getProperties().getPropertyBoolean("Xml.SaveCurrent", true);
153 }
154 }
155
156 private String getId(String type, String id) {
157 if (!iConvertIds)
158 return id.toString();
159 if (iIdConvertor == null)
160 iIdConvertor = new IdConvertor(getModel().getProperties().getProperty("Xml.IdConv"));
161 return iIdConvertor.convert(type, id);
162 }
163
164 private String getId(String type, Number id) {
165 return getId(type, id.toString());
166 }
167
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 @Override
176 public void save() throws Exception {
177 save(null);
178 }
179
180 public void save(File outFile) throws Exception {
181 if (outFile == null)
182 outFile = new File(iOutputFolder, "solution.xml");
183 outFile.getParentFile().mkdirs();
184 sLogger.debug("Writting XML data to:" + outFile);
185
186 Document document = DocumentHelper.createDocument();
187 document.addComment("University Course Timetabling");
188
189 if (iSaveCurrent && !getModel().assignedVariables().isEmpty()) {
190 StringBuffer comments = new StringBuffer("Solution Info:\n");
191 Map<String, String> solutionInfo = (getSolution() == null ? getModel().getInfo() : getSolution().getInfo());
192 for (String key : new TreeSet<String>(solutionInfo.keySet())) {
193 String value = solutionInfo.get(key);
194 comments.append(" " + key + ": " + value + "\n");
195 }
196 document.addComment(comments.toString());
197 }
198
199 Element root = document.addElement("timetable");
200 root.addAttribute("version", "2.5");
201 root.addAttribute("initiative", getModel().getProperties().getProperty("Data.Initiative"));
202 root.addAttribute("term", getModel().getProperties().getProperty("Data.Term"));
203 root.addAttribute("year", String.valueOf(getModel().getYear()));
204 root.addAttribute("created", String.valueOf(new Date()));
205 root.addAttribute("nrDays", String.valueOf(Constants.DAY_CODES.length));
206 root.addAttribute("slotsPerDay", String.valueOf(Constants.SLOTS_PER_DAY));
207 if (!iConvertIds && getModel().getProperties().getProperty("General.SessionId") != null)
208 root.addAttribute("session", getModel().getProperties().getProperty("General.SessionId"));
209 if (iShowNames && !iConvertIds && getModel().getProperties().getProperty("General.SolverGroupId") != null)
210 root.addAttribute("solverGroup", getId("solverGroup", getModel().getProperties().getProperty(
211 "General.SolverGroupId")));
212
213 HashMap<String, Element> roomElements = new HashMap<String, Element>();
214
215 Element roomsEl = root.addElement("rooms");
216 for (RoomConstraint roomConstraint : getModel().getRoomConstraints()) {
217 Element roomEl = roomsEl.addElement("room").addAttribute("id",
218 getId("room", roomConstraint.getResourceId()));
219 roomEl.addAttribute("constraint", "true");
220 if (roomConstraint instanceof DiscouragedRoomConstraint)
221 roomEl.addAttribute("discouraged", "true");
222 if (iShowNames) {
223 roomEl.addAttribute("name", roomConstraint.getRoomName());
224 }
225 if (!iConvertIds && roomConstraint.getBuildingId() != null)
226 roomEl.addAttribute("building", getId("bldg", roomConstraint.getBuildingId()));
227 roomElements.put(getId("room", roomConstraint.getResourceId()), roomEl);
228 roomEl.addAttribute("capacity", String.valueOf(roomConstraint.getCapacity()));
229 if (roomConstraint.getPosX() != null && roomConstraint.getPosY() != null)
230 roomEl.addAttribute("location", roomConstraint.getPosX() + "," + roomConstraint.getPosY());
231 if (roomConstraint.getIgnoreTooFar())
232 roomEl.addAttribute("ignoreTooFar", "true");
233 if (!roomConstraint.getConstraint())
234 roomEl.addAttribute("fake", "true");
235 if (roomConstraint.getSharingModel() != null) {
236 RoomSharingModel sharingModel = roomConstraint.getSharingModel();
237 Element sharingEl = roomEl.addElement("sharing");
238 sharingEl.addElement("pattern").addAttribute("unit", String.valueOf(sharingModel.getStep())).setText(sharingModel.getPreferences());
239 sharingEl.addElement("freeForAll").addAttribute("value",
240 String.valueOf(sharingModel.getFreeForAllPrefChar()));
241 sharingEl.addElement("notAvailable").addAttribute("value",
242 String.valueOf(sharingModel.getNotAvailablePrefChar()));
243 for (int i = 0; i < sharingModel.getNrDepartments(); i++) {
244 sharingEl.addElement("department").addAttribute("value", String.valueOf((char) ('0' + i)))
245 .addAttribute("id", getId("dept", sharingModel.getDepartmentIds()[i]));
246 }
247 }
248 if (roomConstraint.getType() != null && iShowNames)
249 roomEl.addAttribute("type", roomConstraint.getType().toString());
250
251 Map<Long, Integer> travelTimes = getModel().getDistanceMetric().getTravelTimes().get(roomConstraint.getResourceId());
252 if (travelTimes != null)
253 for (Map.Entry<Long, Integer> time: travelTimes.entrySet())
254 roomEl.addElement("travel-time").addAttribute("id", getId("room", time.getKey())).addAttribute("minutes", time.getValue().toString());
255 }
256
257 Element instructorsEl = root.addElement("instructors");
258
259 Element departmentsEl = root.addElement("departments");
260 HashMap<Long, String> depts = new HashMap<Long, String>();
261
262 Element configsEl = (iShowNames ? root.addElement("configurations") : null);
263 HashSet<Configuration> configs = new HashSet<Configuration>();
264
265 Element classesEl = root.addElement("classes");
266 HashMap<Long, Element> classElements = new HashMap<Long, Element>();
267 List<Lecture> vars = new ArrayList<Lecture>(getModel().variables());
268 if (getModel().hasConstantVariables())
269 vars.addAll(getModel().constantVariables());
270 for (Lecture lecture : vars) {
271 Placement placement = lecture.getAssignment();
272 if (lecture.isCommitted() && placement == null)
273 placement = lecture.getInitialAssignment();
274 Placement initialPlacement = lecture.getInitialAssignment();
275 // if (initialPlacement==null) initialPlacement =
276 // (Placement)lecture.getAssignment();
277 Placement bestPlacement = lecture.getBestAssignment();
278 Element classEl = classesEl.addElement("class").addAttribute("id", getId("class", lecture.getClassId()));
279 classElements.put(lecture.getClassId(), classEl);
280 if (iShowNames && lecture.getNote() != null)
281 classEl.addAttribute("note", lecture.getNote());
282 if (iShowNames && !lecture.isCommitted())
283 classEl.addAttribute("ord", String.valueOf(lecture.getOrd()));
284 if (iShowNames && lecture.getSolverGroupId() != null)
285 classEl.addAttribute("solverGroup", getId("solverGroup", lecture.getSolverGroupId()));
286 if (lecture.getParent() == null && lecture.getConfiguration() != null) {
287 if (!iShowNames)
288 classEl.addAttribute("offering", getId("offering", lecture.getConfiguration().getOfferingId()
289 .toString()));
290 classEl.addAttribute("config", getId("config", lecture.getConfiguration().getConfigId().toString()));
291 if (iShowNames && configs.add(lecture.getConfiguration())) {
292 configsEl.addElement("config").addAttribute("id",
293 getId("config", lecture.getConfiguration().getConfigId().toString())).addAttribute("limit",
294 String.valueOf(lecture.getConfiguration().getLimit())).addAttribute("offering",
295 getId("offering", lecture.getConfiguration().getOfferingId().toString()));
296 }
297 }
298 classEl.addAttribute("committed", (lecture.isCommitted() ? "true" : "false"));
299 if (lecture.getParent() != null)
300 classEl.addAttribute("parent", getId("class", lecture.getParent().getClassId()));
301 if (lecture.getSchedulingSubpartId() != null)
302 classEl.addAttribute("subpart", getId("subpart", lecture.getSchedulingSubpartId()));
303 if (iShowNames && lecture.isCommitted() && placement != null && placement.getAssignmentId() != null) {
304 classEl.addAttribute("assignment", getId("assignment", placement.getAssignmentId()));
305 }
306 if (!lecture.isCommitted()) {
307 if (lecture.minClassLimit() == lecture.maxClassLimit()) {
308 classEl.addAttribute("classLimit", String.valueOf(lecture.maxClassLimit()));
309 } else {
310 classEl.addAttribute("minClassLimit", String.valueOf(lecture.minClassLimit()));
311 classEl.addAttribute("maxClassLimit", String.valueOf(lecture.maxClassLimit()));
312 }
313 if (lecture.roomToLimitRatio() != 1.0)
314 classEl.addAttribute("roomToLimitRatio", String.valueOf(lecture.roomToLimitRatio()));
315 }
316 if (lecture.getNrRooms() != 1)
317 classEl.addAttribute("nrRooms", String.valueOf(lecture.getNrRooms()));
318 if (iShowNames)
319 classEl.addAttribute("name", lecture.getName());
320 if (lecture.getDeptSpreadConstraint() != null) {
321 classEl.addAttribute("department", getId("dept", lecture.getDeptSpreadConstraint().getDepartmentId()));
322 depts.put(lecture.getDeptSpreadConstraint().getDepartmentId(), lecture.getDeptSpreadConstraint()
323 .getName());
324 }
325 if (lecture.getScheduler() != null)
326 classEl.addAttribute("scheduler", getId("dept", lecture.getScheduler()));
327 for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
328 Element instrEl = classEl.addElement("instructor")
329 .addAttribute("id", getId("inst", ic.getResourceId()));
330 if ((lecture.isCommitted() || iSaveCurrent) && placement != null)
331 instrEl.addAttribute("solution", "true");
332 if (iSaveInitial && initialPlacement != null)
333 instrEl.addAttribute("initial", "true");
334 if (iSaveBest && bestPlacement != null && !bestPlacement.equals(placement))
335 instrEl.addAttribute("best", "true");
336 }
337 for (RoomLocation rl : lecture.roomLocations()) {
338 Element roomLocationEl = classEl.addElement("room");
339 roomLocationEl.addAttribute("id", getId("room", rl.getId()));
340 roomLocationEl.addAttribute("pref", String.valueOf(rl.getPreference()));
341 if ((lecture.isCommitted() || iSaveCurrent) && placement != null
342 && placement.hasRoomLocation(rl.getId()))
343 roomLocationEl.addAttribute("solution", "true");
344 if (iSaveInitial && initialPlacement != null && initialPlacement.hasRoomLocation(rl.getId()))
345 roomLocationEl.addAttribute("initial", "true");
346 if (iSaveBest && bestPlacement != null && !bestPlacement.equals(placement)
347 && bestPlacement.hasRoomLocation(rl.getId()))
348 roomLocationEl.addAttribute("best", "true");
349 if (!roomElements.containsKey(getId("room", rl.getId()))) {
350 // room location without room constraint
351 Element roomEl = roomsEl.addElement("room").addAttribute("id", getId("room", rl.getId()));
352 roomEl.addAttribute("constraint", "false");
353 if (!iConvertIds && rl.getBuildingId() != null)
354 roomEl.addAttribute("building", getId("bldg", rl.getBuildingId()));
355 if (iShowNames) {
356 roomEl.addAttribute("name", rl.getName());
357 }
358 roomElements.put(getId("room", rl.getId()), roomEl);
359 roomEl.addAttribute("capacity", String.valueOf(rl.getRoomSize()));
360 if (rl.getPosX() != null && rl.getPosY() != null)
361 roomEl.addAttribute("location", rl.getPosX() + "," + rl.getPosY());
362 if (rl.getIgnoreTooFar())
363 roomEl.addAttribute("ignoreTooFar", "true");
364 }
365 }
366 boolean first = true;
367 Set<Long> dp = new HashSet<Long>();
368 for (TimeLocation tl : lecture.timeLocations()) {
369 Element timeLocationEl = classEl.addElement("time");
370 timeLocationEl.addAttribute("days", sDF[7].format(Long.parseLong(Integer
371 .toBinaryString(tl.getDayCode()))));
372 timeLocationEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
373 timeLocationEl.addAttribute("length", String.valueOf(tl.getLength()));
374 timeLocationEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
375 if (iShowNames) {
376 timeLocationEl.addAttribute("pref", String.valueOf(tl.getPreference()));
377 timeLocationEl.addAttribute("npref", String.valueOf(tl.getNormalizedPreference()));
378 } else {
379 timeLocationEl.addAttribute("pref", String.valueOf(tl.getNormalizedPreference()));
380 }
381 if (!iConvertIds && tl.getTimePatternId() != null)
382 timeLocationEl.addAttribute("pattern", getId("pat", tl.getTimePatternId()));
383 if (tl.getDatePatternId() != null && dp.add(tl.getDatePatternId())) {
384 Element dateEl = classEl.addElement("date");
385 dateEl.addAttribute("id", getId("dpat", String.valueOf(tl.getDatePatternId())));
386 if (iShowNames)
387 dateEl.addAttribute("name", tl.getDatePatternName());
388 dateEl.addAttribute("pattern", bitset2string(tl.getWeekCode()));
389 }
390 if (tl.getDatePatternPreference() != 0)
391 timeLocationEl.addAttribute("datePref", String.valueOf(tl.getDatePatternPreference()));
392 if (tl.getTimePatternId() == null && first) {
393 if (iShowNames)
394 classEl.addAttribute("datePatternName", tl.getDatePatternName());
395 classEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
396 first = false;
397 }
398 if (tl.getDatePatternId() != null) {
399 timeLocationEl.addAttribute("date", getId("dpat", String.valueOf(tl.getDatePatternId())));
400 }
401 if ((lecture.isCommitted() || iSaveCurrent) && placement != null
402 && placement.getTimeLocation().equals(tl))
403 timeLocationEl.addAttribute("solution", "true");
404 if (iSaveInitial && initialPlacement != null && initialPlacement.getTimeLocation().equals(tl))
405 timeLocationEl.addAttribute("initial", "true");
406 if (iSaveBest && bestPlacement != null && !bestPlacement.equals(placement)
407 && bestPlacement.getTimeLocation().equals(tl))
408 timeLocationEl.addAttribute("best", "true");
409 }
410 }
411
412 for (InstructorConstraint ic : getModel().getInstructorConstraints()) {
413 if (iShowNames || ic.isIgnoreDistances()) {
414 Element instrEl = instructorsEl.addElement("instructor").addAttribute("id",
415 getId("inst", ic.getResourceId()));
416 if (iShowNames) {
417 if (ic.getPuid() != null && ic.getPuid().length() > 0)
418 instrEl.addAttribute("puid", ic.getPuid());
419 instrEl.addAttribute("name", ic.getName());
420 if (ic.getType() != null && iShowNames)
421 instrEl.addAttribute("type", ic.getType().toString());
422 }
423 if (ic.isIgnoreDistances()) {
424 instrEl.addAttribute("ignDist", "true");
425 }
426 }
427 if (ic.getUnavailabilities() != null) {
428 for (Placement placement: ic.getUnavailabilities()) {
429 Lecture lecture = placement.variable();
430 Element classEl = classElements.get(lecture.getClassId());
431 classEl.addElement("instructor").addAttribute("id", getId("inst", ic.getResourceId())).addAttribute("solution", "true");
432 }
433 }
434 }
435 if (instructorsEl.elements().isEmpty())
436 root.remove(instructorsEl);
437
438 Element grConstraintsEl = root.addElement("groupConstraints");
439 for (GroupConstraint gc : getModel().getGroupConstraints()) {
440 Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id",
441 getId("gr", String.valueOf(gc.getId())));
442 grEl.addAttribute("type", gc.getType().reference());
443 grEl.addAttribute("pref", gc.getPrologPreference());
444 for (Lecture l : gc.variables()) {
445 grEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
446 }
447 }
448 for (SpreadConstraint spread : getModel().getSpreadConstraints()) {
449 Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id",
450 getId("gr", String.valueOf(spread.getId())));
451 grEl.addAttribute("type", "SPREAD");
452 grEl.addAttribute("pref", Constants.sPreferenceRequired);
453 if (iShowNames)
454 grEl.addAttribute("name", spread.getName());
455 for (Lecture l : spread.variables()) {
456 grEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
457 }
458 }
459 for (Constraint<Lecture, Placement> c : getModel().constraints()) {
460 if (c instanceof MinimizeNumberOfUsedRoomsConstraint) {
461 Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id",
462 getId("gr", String.valueOf(c.getId())));
463 grEl.addAttribute("type", "MIN_ROOM_USE");
464 grEl.addAttribute("pref", Constants.sPreferenceRequired);
465 for (Lecture l : c.variables()) {
466 grEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
467 }
468 }
469 if (c instanceof MinimizeNumberOfUsedGroupsOfTime) {
470 Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id",
471 getId("gr", String.valueOf(c.getId())));
472 grEl.addAttribute("type", ((MinimizeNumberOfUsedGroupsOfTime) c).getConstraintName());
473 grEl.addAttribute("pref", Constants.sPreferenceRequired);
474 for (Lecture l : c.variables()) {
475 grEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
476 }
477 }
478 if (c instanceof IgnoreStudentConflictsConstraint) {
479 Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id", getId("gr", String.valueOf(c.getId())));
480 grEl.addAttribute("type", IgnoreStudentConflictsConstraint.REFERENCE);
481 grEl.addAttribute("pref", Constants.sPreferenceRequired);
482 for (Lecture l : c.variables()) {
483 grEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
484 }
485 }
486 }
487 for (ClassLimitConstraint clc : getModel().getClassLimitConstraints()) {
488 Element grEl = grConstraintsEl.addElement("constraint").addAttribute("id",
489 getId("gr", String.valueOf(clc.getId())));
490 grEl.addAttribute("type", "CLASS_LIMIT");
491 grEl.addAttribute("pref", Constants.sPreferenceRequired);
492 if (clc.getParentLecture() != null) {
493 grEl.addElement("parentClass").addAttribute("id", getId("class", clc.getParentLecture().getClassId()));
494 } else
495 grEl.addAttribute("courseLimit", String.valueOf(clc.classLimit() - clc.getClassLimitDelta()));
496 if (clc.getClassLimitDelta() != 0)
497 grEl.addAttribute("delta", String.valueOf(clc.getClassLimitDelta()));
498 if (iShowNames)
499 grEl.addAttribute("name", clc.getName());
500 for (Lecture l : clc.variables()) {
501 grEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
502 }
503 }
504 for (FlexibleConstraint gc : getModel().getFlexibleConstraints()) {
505 Element flEl = grConstraintsEl.addElement("constraint").addAttribute("id",
506 getId("gr", String.valueOf(gc.getId())));
507 flEl.addAttribute("reference", gc.getReference());
508 flEl.addAttribute("owner", gc.getOwner());
509 flEl.addAttribute("pref", gc.getPrologPreference());
510 flEl.addAttribute("type", gc.getType().toString());
511 for (Lecture l : gc.variables()) {
512 flEl.addElement("class").addAttribute("id", getId("class", l.getClassId()));
513 }
514 }
515
516 HashMap<Student, List<String>> students = new HashMap<Student, List<String>>();
517 for (Lecture lecture : vars) {
518 for (Student student : lecture.students()) {
519 List<String> enrls = students.get(student);
520 if (enrls == null) {
521 enrls = new ArrayList<String>();
522 students.put(student, enrls);
523 }
524 enrls.add(getId("class", lecture.getClassId()));
525 }
526 }
527
528 Element studentsEl = root.addElement("students");
529 for (Student student: new TreeSet<Student>(students.keySet())) {
530 Element stEl = studentsEl.addElement("student").addAttribute("id", getId("student", student.getId()));
531 if (iShowNames) {
532 if (student.getAcademicArea() != null)
533 stEl.addAttribute("area", student.getAcademicArea());
534 if (student.getAcademicClassification() != null)
535 stEl.addAttribute("classification", student.getAcademicClassification());
536 if (student.getMajor() != null)
537 stEl.addAttribute("major", student.getMajor());
538 if (student.getCurriculum() != null)
539 stEl.addAttribute("curriculum", student.getCurriculum());
540 }
541 for (Map.Entry<Long, Double> entry : student.getOfferingsMap().entrySet()) {
542 Long offeringId = entry.getKey();
543 Double weight = entry.getValue();
544 Element offEl = stEl.addElement("offering")
545 .addAttribute("id", getId("offering", offeringId.toString()));
546 if (weight.doubleValue() != 1.0)
547 offEl.addAttribute("weight", sStudentWeightFormat.format(weight));
548 Double priority = student.getPriority(offeringId);
549 if (priority != null)
550 offEl.addAttribute("priority", priority.toString());
551 }
552 if (iExportStudentSectioning || getModel().unassignedVariables().isEmpty()
553 || student.getOfferingsMap().isEmpty()) {
554 List<String> lectures = students.get(student);
555 Collections.sort(lectures);
556 for (String classId : lectures) {
557 stEl.addElement("class").addAttribute("id", classId);
558 }
559 }
560 Map<Long, Set<Lecture>> canNotEnroll = student.canNotEnrollSections();
561 if (canNotEnroll != null) {
562 for (Set<Lecture> canNotEnrollLects: canNotEnroll.values()) {
563 for (Iterator<Lecture> i3 = canNotEnrollLects.iterator(); i3.hasNext();) {
564 stEl.addElement("prohibited-class")
565 .addAttribute("id", getId("class", (i3.next()).getClassId()));
566 }
567 }
568 }
569
570 if (student.getCommitedPlacements() != null) {
571 for (Placement placement : student.getCommitedPlacements()) {
572 stEl.addElement("class").addAttribute("id", getId("class", placement.variable().getClassId()));
573 }
574 }
575
576 if (student.getInstructor() != null)
577 stEl.addAttribute("instructor", getId("inst", student.getInstructor().getResourceId()));
578 }
579
580 if (getModel().getProperties().getPropertyInt("MPP.GenTimePert", 0) > 0) {
581 Element perturbationsEl = root.addElement("perturbations");
582 int nrChanges = getModel().getProperties().getPropertyInt("MPP.GenTimePert", 0);
583 List<Lecture> lectures = new ArrayList<Lecture>();
584 while (lectures.size() < nrChanges) {
585 Lecture lecture = ToolBox.random(getModel().assignedVariables());
586 if (lecture.isCommitted() || lecture.timeLocations().size() <= 1 || lectures.contains(lecture))
587 continue;
588 Placement placement = lecture.getAssignment();
589 TimeLocation tl = placement.getTimeLocation();
590 perturbationsEl.addElement("class").addAttribute("id", getId("class", lecture.getClassId()))
591 .addAttribute("days", sDF[7].format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))))
592 .addAttribute("start", String.valueOf(tl.getStartSlot())).addAttribute("length",
593 String.valueOf(tl.getLength()));
594 lectures.add(lecture);
595 }
596 }
597
598 for (Map.Entry<Long, String> entry : depts.entrySet()) {
599 Long id = entry.getKey();
600 String name = entry.getValue();
601 if (iShowNames) {
602 departmentsEl.addElement("department").addAttribute("id", getId("dept", id.toString())).addAttribute(
603 "name", name);
604 }
605 }
606 if (departmentsEl.elements().isEmpty())
607 root.remove(departmentsEl);
608
609 if (iShowNames) {
610 Progress.getInstance(getModel()).save(root);
611
612 try {
613 getSolver().getClass().getMethod("save", new Class[] { Element.class }).invoke(getSolver(),
614 new Object[] { root });
615 } catch (Exception e) {
616 }
617 }
618
619 FileOutputStream fos = null;
620 try {
621 fos = new FileOutputStream(outFile);
622 (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(document);
623 fos.flush();
624 fos.close();
625 fos = null;
626 } finally {
627 try {
628 if (fos != null)
629 fos.close();
630 } catch (IOException e) {
631 }
632 }
633
634 if (iConvertIds)
635 iIdConvertor.save();
636 }
637 }