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