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