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