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