001package net.sf.cpsolver.studentsct; 002 003import java.io.File; 004import java.util.ArrayList; 005import java.util.BitSet; 006import java.util.HashSet; 007import java.util.HashMap; 008import java.util.Iterator; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012 013import net.sf.cpsolver.coursett.model.Placement; 014import net.sf.cpsolver.coursett.model.RoomLocation; 015import net.sf.cpsolver.coursett.model.TimeLocation; 016import net.sf.cpsolver.ifs.model.Constraint; 017import net.sf.cpsolver.ifs.util.DistanceMetric; 018import net.sf.cpsolver.ifs.util.Progress; 019import net.sf.cpsolver.studentsct.filter.StudentFilter; 020import net.sf.cpsolver.studentsct.model.AcademicAreaCode; 021import net.sf.cpsolver.studentsct.model.Choice; 022import net.sf.cpsolver.studentsct.model.Config; 023import net.sf.cpsolver.studentsct.model.Course; 024import net.sf.cpsolver.studentsct.model.CourseRequest; 025import net.sf.cpsolver.studentsct.model.Enrollment; 026import net.sf.cpsolver.studentsct.model.FreeTimeRequest; 027import net.sf.cpsolver.studentsct.model.Offering; 028import net.sf.cpsolver.studentsct.model.Request; 029import net.sf.cpsolver.studentsct.model.Section; 030import net.sf.cpsolver.studentsct.model.Student; 031import net.sf.cpsolver.studentsct.model.Subpart; 032import net.sf.cpsolver.studentsct.reservation.CourseReservation; 033import net.sf.cpsolver.studentsct.reservation.CurriculumReservation; 034import net.sf.cpsolver.studentsct.reservation.DummyReservation; 035import net.sf.cpsolver.studentsct.reservation.GroupReservation; 036import net.sf.cpsolver.studentsct.reservation.IndividualReservation; 037import net.sf.cpsolver.studentsct.reservation.Reservation; 038 039import org.dom4j.Document; 040import org.dom4j.DocumentException; 041import org.dom4j.Element; 042import org.dom4j.io.SAXReader; 043 044/** 045 * Load student sectioning model from an XML file. 046 * 047 * <br> 048 * <br> 049 * Parameters: 050 * <table border='1'> 051 * <tr> 052 * <th>Parameter</th> 053 * <th>Type</th> 054 * <th>Comment</th> 055 * </tr> 056 * <tr> 057 * <td>General.Input</td> 058 * <td>{@link String}</td> 059 * <td>Path of an XML file to be loaded</td> 060 * </tr> 061 * <tr> 062 * <td>Xml.LoadBest</td> 063 * <td>{@link Boolean}</td> 064 * <td>If true, load best assignments</td> 065 * </tr> 066 * <tr> 067 * <td>Xml.LoadInitial</td> 068 * <td>{@link Boolean}</td> 069 * <td>If false, load initial assignments</td> 070 * </tr> 071 * <tr> 072 * <td>Xml.LoadCurrent</td> 073 * <td>{@link Boolean}</td> 074 * <td>If true, load current assignments</td> 075 * </tr> 076 * <tr> 077 * <td>Xml.LoadOfferings</td> 078 * <td>{@link Boolean}</td> 079 * <td>If true, load offerings (and their stucture, i.e., courses, 080 * configurations, subparts and sections)</td> 081 * </tr> 082 * <tr> 083 * <td>Xml.LoadStudents</td> 084 * <td>{@link Boolean}</td> 085 * <td>If true, load students (and their requests)</td> 086 * </tr> 087 * <tr> 088 * <td>Xml.StudentFilter</td> 089 * <td>{@link StudentFilter}</td> 090 * <td>If provided, students are filtered by the given student filter</td> 091 * </tr> 092 * </table> 093 * 094 * <br> 095 * <br> 096 * Usage:<br> 097 * <code> 098 * StudentSectioningModel model = new StudentSectioningModel(cfg);<br> 099 * new StudentSectioningXMLLoader(model).load();<br> 100 * </code> 101 * 102 * @version StudentSct 1.2 (Student Sectioning)<br> 103 * Copyright (C) 2007 - 2010 Tomáš Müller<br> 104 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 105 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 106 * <br> 107 * This library is free software; you can redistribute it and/or modify 108 * it under the terms of the GNU Lesser General Public License as 109 * published by the Free Software Foundation; either version 3 of the 110 * License, or (at your option) any later version. <br> 111 * <br> 112 * This library is distributed in the hope that it will be useful, but 113 * WITHOUT ANY WARRANTY; without even the implied warranty of 114 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 115 * Lesser General Public License for more details. <br> 116 * <br> 117 * You should have received a copy of the GNU Lesser General Public 118 * License along with this library; if not see 119 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 120 */ 121 122public class StudentSectioningXMLLoader extends StudentSectioningLoader { 123 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger 124 .getLogger(StudentSectioningXMLLoader.class); 125 126 private File iInputFile; 127 private File iTimetableFile = null; 128 private boolean iLoadBest = false; 129 private boolean iLoadInitial = false; 130 private boolean iLoadCurrent = false; 131 private boolean iLoadOfferings = true; 132 private boolean iLoadStudents = true; 133 private StudentFilter iStudentFilter = null; 134 135 /** 136 * Constructor 137 * 138 * @param model 139 * student sectioning model 140 */ 141 public StudentSectioningXMLLoader(StudentSectioningModel model) { 142 super(model); 143 iInputFile = new File(getModel().getProperties().getProperty("General.Input", 144 "." + File.separator + "solution.xml")); 145 if (getModel().getProperties().getProperty("General.InputTimetable") != null) 146 iTimetableFile = new File(getModel().getProperties().getProperty("General.InputTimetable")); 147 iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true); 148 iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true); 149 iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true); 150 iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true); 151 iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true); 152 if (getModel().getProperties().getProperty("Xml.StudentFilter") != null) { 153 try { 154 iStudentFilter = (StudentFilter) Class.forName( 155 getModel().getProperties().getProperty("Xml.StudentFilter")).getConstructor(new Class[] {}) 156 .newInstance(new Object[] {}); 157 } catch (Exception e) { 158 sLogger.error("Unable to create student filter, reason: " + e.getMessage(), e); 159 } 160 } 161 } 162 163 /** Set input file (e.g., if it is not set by General.Input property) */ 164 public void setInputFile(File inputFile) { 165 iInputFile = inputFile; 166 } 167 168 /** Set student filter */ 169 public void setStudentFilter(StudentFilter filter) { 170 iStudentFilter = filter; 171 } 172 173 /** Set whether to load students */ 174 public void setLoadStudents(boolean loadStudents) { 175 iLoadStudents = loadStudents; 176 } 177 178 /** Set whether to load offerings */ 179 public void setLoadOfferings(boolean loadOfferings) { 180 iLoadOfferings = loadOfferings; 181 } 182 183 /** Create BitSet from a bit string */ 184 private static BitSet createBitSet(String bitString) { 185 BitSet ret = new BitSet(bitString.length()); 186 for (int i = 0; i < bitString.length(); i++) 187 if (bitString.charAt(i) == '1') 188 ret.set(i); 189 return ret; 190 } 191 192 /** Load the file */ 193 @Override 194 public void load() throws Exception { 195 sLogger.debug("Reading XML data from " + iInputFile); 196 197 Document document = (new SAXReader()).read(iInputFile); 198 Element root = document.getRootElement(); 199 200 load(root); 201 } 202 203 /** 204 * Load data from the given XML root 205 * @param root document root 206 * @throws DocumentException 207 */ 208 protected void load(Element root) throws DocumentException { 209 sLogger.debug("Root element: " + root.getName()); 210 if (!"sectioning".equals(root.getName())) { 211 sLogger.error("Given XML file is not student sectioning problem."); 212 return; 213 } 214 215 if (iLoadOfferings && getModel().getDistanceConflict() != null && root.element("travel-times") != null) 216 loadTravelTimes(root.element("travel-times"), getModel().getDistanceConflict().getDistanceMetric()); 217 218 Map<Long, Placement> timetable = null; 219 if (iTimetableFile != null) { 220 sLogger.info("Reading timetable from " + iTimetableFile + " ..."); 221 Document timetableDocument = (new SAXReader()).read(iTimetableFile); 222 Element timetableRoot = timetableDocument.getRootElement(); 223 if (!"timetable".equals(timetableRoot.getName())) { 224 sLogger.error("Given XML file is not course timetabling problem."); 225 return; 226 } 227 timetable = loadTimetable(timetableRoot); 228 } 229 230 Progress.getInstance(getModel()).load(root, true); 231 Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 232 233 if (root.attributeValue("term") != null) 234 getModel().getProperties().setProperty("Data.Term", root.attributeValue("term")); 235 if (root.attributeValue("year") != null) 236 getModel().getProperties().setProperty("Data.Year", root.attributeValue("year")); 237 if (root.attributeValue("initiative") != null) 238 getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative")); 239 240 Map<Long, Offering> offeringTable = new HashMap<Long, Offering>(); 241 Map<Long, Course> courseTable = new HashMap<Long, Course>(); 242 243 if (iLoadOfferings && root.element("offerings") != null) { 244 loadOfferings(root.element("offerings"), offeringTable, courseTable, timetable); 245 } else { 246 for (Offering offering : getModel().getOfferings()) { 247 offeringTable.put(new Long(offering.getId()), offering); 248 for (Course course : offering.getCourses()) { 249 courseTable.put(new Long(course.getId()), course); 250 } 251 } 252 } 253 254 if (iLoadStudents && root.element("students") != null) { 255 loadStudents(root.element("students"), offeringTable, courseTable); 256 } 257 258 if (iLoadOfferings && root.element("constraints") != null) 259 loadLinkedSectiond(root.element("constraints"), offeringTable); 260 261 sLogger.debug("Model successfully loaded."); 262 } 263 264 /** 265 * Load offerings 266 * @param offeringsEl offerings element 267 * @param offeringTable offering table 268 * @param courseTable course table 269 * @param timetable provided timetable (null if to be loaded from the given document) 270 */ 271 protected void loadOfferings(Element offeringsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable, Map<Long, Placement> timetable) { 272 HashMap<Long, Config> configTable = new HashMap<Long, Config>(); 273 HashMap<Long, Subpart> subpartTable = new HashMap<Long, Subpart>(); 274 HashMap<Long, Section> sectionTable = new HashMap<Long, Section>(); 275 for (Iterator<?> i = offeringsEl.elementIterator("offering"); i.hasNext();) { 276 Element offeringEl = (Element) i.next(); 277 Offering offering = new Offering( 278 Long.parseLong(offeringEl.attributeValue("id")), 279 offeringEl.attributeValue("name", "O" + offeringEl.attributeValue("id"))); 280 offeringTable.put(new Long(offering.getId()), offering); 281 getModel().addOffering(offering); 282 283 for (Iterator<?> j = offeringEl.elementIterator("course"); j.hasNext();) { 284 Element courseEl = (Element) j.next(); 285 Course course = loadCourse(courseEl, offering); 286 courseTable.put(new Long(course.getId()), course); 287 } 288 289 for (Iterator<?> j = offeringEl.elementIterator("config"); j.hasNext();) { 290 Element configEl = (Element) j.next(); 291 Config config = loadConfig(configEl, offering, subpartTable, sectionTable, timetable); 292 configTable.put(config.getId(), config); 293 } 294 295 for (Iterator<?> j = offeringEl.elementIterator("reservation"); j.hasNext(); ) { 296 Element reservationEl = (Element)j.next(); 297 loadReservation(reservationEl, offering, configTable, sectionTable); 298 } 299 } 300 } 301 302 /** 303 * Load course 304 * @param courseEl course element 305 * @param offering parent offering 306 * @return loaded course 307 */ 308 protected Course loadCourse(Element courseEl, Offering offering) { 309 Course course = new Course( 310 Long.parseLong(courseEl.attributeValue("id")), 311 courseEl.attributeValue("subjectArea", ""), 312 courseEl.attributeValue("courseNbr", "C" + courseEl.attributeValue("id")), 313 offering, Integer.parseInt(courseEl.attributeValue("limit", "-1")), 314 Integer.parseInt(courseEl.attributeValue("projected", "0"))); 315 return course; 316 } 317 318 /** 319 * Load config 320 * @param configEl config element 321 * @param offering parent offering 322 * @param subpartTable subpart table (of the offering) 323 * @param sectionTable section table (of the offering) 324 * @param timetable provided timetable 325 * @return loaded config 326 */ 327 protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 328 Config config = new Config 329 (Long.parseLong(configEl.attributeValue("id")), 330 Integer.parseInt(configEl.attributeValue("limit", "-1")), 331 configEl.attributeValue("name", "G" + configEl.attributeValue("id")), 332 offering); 333 for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext();) { 334 Element subpartEl = (Element) k.next(); 335 Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable); 336 subpartTable.put(new Long(subpart.getId()), subpart); 337 } 338 return config; 339 } 340 341 /** 342 * Load subpart 343 * @param subpartEl supart element 344 * @param config parent config 345 * @param subpartTable subpart table (of the offering) 346 * @param sectionTable section table (of the offering) 347 * @param timetable provided timetable 348 * @return loaded subpart 349 */ 350 protected Subpart loadSubpart(Element subpartEl, Config config, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 351 Subpart parentSubpart = null; 352 if (subpartEl.attributeValue("parent") != null) 353 parentSubpart = subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent"))); 354 Subpart subpart = new Subpart( 355 Long.parseLong(subpartEl.attributeValue("id")), 356 subpartEl.attributeValue("itype"), 357 subpartEl.attributeValue("name", "P" + subpartEl.attributeValue("id")), 358 config, 359 parentSubpart); 360 subpart.setAllowOverlap("true".equals(subpartEl.attributeValue("allowOverlap", "false"))); 361 362 363 for (Iterator<?> l = subpartEl.elementIterator("section"); l.hasNext();) { 364 Element sectionEl = (Element) l.next(); 365 Section section = loadSection(sectionEl, subpart, sectionTable, timetable); 366 sectionTable.put(new Long(section.getId()), section); 367 } 368 369 return subpart; 370 } 371 372 /** 373 * Load section 374 * @param sectionEl section element 375 * @param subpart parent subpart 376 * @param sectionTable section table (of the offering) 377 * @param timetable provided timetable 378 * @return loaded section 379 */ 380 protected Section loadSection(Element sectionEl, Subpart subpart, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) { 381 Section parentSection = null; 382 if (sectionEl.attributeValue("parent") != null) 383 parentSection = sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent"))); 384 Placement placement = null; 385 if (timetable != null) { 386 placement = timetable.get(Long.parseLong(sectionEl.attributeValue("id"))); 387 } else { 388 TimeLocation time = null; 389 Element timeEl = sectionEl.element("time"); 390 if (timeEl != null) { 391 time = new TimeLocation( 392 Integer.parseInt(timeEl.attributeValue("days"), 2), 393 Integer.parseInt(timeEl.attributeValue("start")), 394 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 395 timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")), 396 timeEl.attributeValue("datePatternName", ""), 397 createBitSet(timeEl.attributeValue("dates")), 398 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 399 if (timeEl.attributeValue("pattern") != null) 400 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 401 } 402 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 403 for (Iterator<?> m = sectionEl.elementIterator("room"); m.hasNext();) { 404 Element roomEl = (Element) m.next(); 405 Double posX = null, posY = null; 406 if (roomEl.attributeValue("location") != null) { 407 String loc = roomEl.attributeValue("location"); 408 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 409 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 410 } 411 RoomLocation room = new RoomLocation( 412 Long.valueOf(roomEl.attributeValue("id")), 413 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 414 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 415 0, Integer.parseInt(roomEl.attributeValue("capacity")), 416 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 417 rooms.add(room); 418 } 419 placement = (time == null ? null : new Placement(null, time, rooms)); 420 } 421 422 Section section = new Section( 423 Long.parseLong(sectionEl.attributeValue("id")), 424 Integer.parseInt(sectionEl.attributeValue("limit")), 425 sectionEl.attributeValue("name", "S" + sectionEl.attributeValue("id")), 426 subpart, placement, sectionEl.attributeValue("instructorIds"), 427 sectionEl.attributeValue("instructorNames"), parentSection); 428 429 section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0"))); 430 section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0"))); 431 for (Iterator<?> m = sectionEl.elementIterator("cname"); m.hasNext(); ) { 432 Element cNameEl = (Element)m.next(); 433 section.setName(Long.parseLong(cNameEl.attributeValue("id")), cNameEl.getText()); 434 } 435 Element ignoreEl = sectionEl.element("no-conflicts"); 436 if (ignoreEl != null) { 437 for (Iterator<?> m = ignoreEl.elementIterator("section"); m.hasNext(); ) 438 section.addIgnoreConflictWith(Long.parseLong(((Element)m.next()).attributeValue("id"))); 439 } 440 441 return section; 442 } 443 444 /** 445 * Load reservation 446 * @param reservationEl reservation element 447 * @param offering parent offering 448 * @param configTable config table (of the offering) 449 * @param sectionTable section table (of the offering) 450 * @return loaded reservation 451 */ 452 protected Reservation loadReservation(Element reservationEl, Offering offering, HashMap<Long, Config> configTable, HashMap<Long, Section> sectionTable) { 453 Reservation r = null; 454 if ("individual".equals(reservationEl.attributeValue("type"))) { 455 Set<Long> studentIds = new HashSet<Long>(); 456 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 457 Element studentEl = (Element)k.next(); 458 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 459 } 460 r = new IndividualReservation(Long.valueOf(reservationEl.attributeValue("id")), offering, studentIds); 461 } else if ("group".equals(reservationEl.attributeValue("type"))) { 462 Set<Long> studentIds = new HashSet<Long>(); 463 for (Iterator<?> k = reservationEl.elementIterator("student"); k.hasNext(); ) { 464 Element studentEl = (Element)k.next(); 465 studentIds.add(Long.parseLong(studentEl.attributeValue("id"))); 466 } 467 r = new GroupReservation(Long.valueOf(reservationEl.attributeValue("id")), 468 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 469 offering, studentIds); 470 } else if ("curriculum".equals(reservationEl.attributeValue("type"))) { 471 List<String> classifications = new ArrayList<String>(); 472 for (Iterator<?> k = reservationEl.elementIterator("classification"); k.hasNext(); ) { 473 Element clasfEl = (Element)k.next(); 474 classifications.add(clasfEl.attributeValue("code")); 475 } 476 List<String> majors = new ArrayList<String>(); 477 for (Iterator<?> k = reservationEl.elementIterator("major"); k.hasNext(); ) { 478 Element majorEl = (Element)k.next(); 479 majors.add(majorEl.attributeValue("code")); 480 } 481 r = new CurriculumReservation(Long.valueOf(reservationEl.attributeValue("id")), 482 Double.parseDouble(reservationEl.attributeValue("limit", "-1")), 483 offering, 484 reservationEl.attributeValue("area"), 485 classifications, majors); 486 } else if ("course".equals(reservationEl.attributeValue("type"))) { 487 long courseId = Long.parseLong(reservationEl.attributeValue("course")); 488 for (Course course: offering.getCourses()) { 489 if (course.getId() == courseId) 490 r = new CourseReservation(Long.valueOf(reservationEl.attributeValue("id")), course); 491 } 492 } else if ("dummy".equals(reservationEl.attributeValue("type"))) { 493 r = new DummyReservation(offering); 494 } 495 if (r == null) { 496 sLogger.error("Unknown reservation type "+ reservationEl.attributeValue("type")); 497 return null; 498 } 499 r.setExpired("true".equals(reservationEl.attributeValue("expired", "false"))); 500 for (Iterator<?> k = reservationEl.elementIterator("config"); k.hasNext(); ) { 501 Element configEl = (Element)k.next(); 502 r.addConfig(configTable.get(Long.parseLong(configEl.attributeValue("id")))); 503 } 504 for (Iterator<?> k = reservationEl.elementIterator("section"); k.hasNext(); ) { 505 Element sectionEl = (Element)k.next(); 506 r.addSection(sectionTable.get(Long.parseLong(sectionEl.attributeValue("id")))); 507 } 508 return r; 509 } 510 511 /** 512 * Load given timetable 513 * @param timetableRoot document root in the course timetabling XML format 514 * @return loaded timetable (map class id: assigned placement) 515 */ 516 protected Map<Long, Placement> loadTimetable(Element timetableRoot) { 517 Map<Long, Placement> timetable = new HashMap<Long, Placement>(); 518 HashMap<Long, RoomLocation> rooms = new HashMap<Long, RoomLocation>(); 519 for (Iterator<?> i = timetableRoot.element("rooms").elementIterator("room"); i.hasNext();) { 520 Element roomEl = (Element)i.next(); 521 Long roomId = Long.valueOf(roomEl.attributeValue("id")); 522 Double posX = null, posY = null; 523 if (roomEl.attributeValue("location") != null) { 524 String loc = roomEl.attributeValue("location"); 525 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 526 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 527 } 528 RoomLocation room = new RoomLocation( 529 Long.valueOf(roomEl.attributeValue("id")), 530 roomEl.attributeValue("name", "R" + roomEl.attributeValue("id")), 531 roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building")), 532 0, Integer.parseInt(roomEl.attributeValue("capacity")), 533 posX, posY, "true".equals(roomEl.attributeValue("ignoreTooFar")), null); 534 rooms.put(roomId, room); 535 } 536 for (Iterator<?> i = timetableRoot.element("classes").elementIterator("class"); i.hasNext();) { 537 Element classEl = (Element)i.next(); 538 Long classId = Long.valueOf(classEl.attributeValue("id")); 539 TimeLocation time = null; 540 Element timeEl = null; 541 for (Iterator<?> j = classEl.elementIterator("time"); j.hasNext(); ) { 542 Element e = (Element)j.next(); 543 if ("true".equals(e.attributeValue("solution", "false"))) { timeEl = e; break; } 544 } 545 if (timeEl != null) { 546 time = new TimeLocation( 547 Integer.parseInt(timeEl.attributeValue("days"), 2), 548 Integer.parseInt(timeEl.attributeValue("start")), 549 Integer.parseInt(timeEl.attributeValue("length")), 0, 0, 550 classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")), 551 classEl.attributeValue("datePatternName", ""), createBitSet(classEl.attributeValue("dates")), 552 Integer.parseInt(timeEl.attributeValue("breakTime", "0"))); 553 if (timeEl.attributeValue("pattern") != null) 554 time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern"))); 555 } 556 List<RoomLocation> room = new ArrayList<RoomLocation>(); 557 for (Iterator<?> j = classEl.elementIterator("room"); j.hasNext();) { 558 Element roomEl = (Element) j.next(); 559 if (!"true".equals(roomEl.attributeValue("solution", "false"))) continue; 560 room.add(rooms.get(Long.valueOf(roomEl.attributeValue("id")))); 561 } 562 Placement placement = (time == null ? null : new Placement(null, time, room)); 563 if (placement != null) 564 timetable.put(classId, placement); 565 } 566 return timetable; 567 } 568 569 /** 570 * Load travel times 571 * @param travelTimesEl travel-time element 572 * @param metric distance metric to be populated 573 */ 574 protected void loadTravelTimes(Element travelTimesEl, DistanceMetric metric) { 575 for (Iterator<?> i = travelTimesEl.elementIterator("travel-time"); i.hasNext();) { 576 Element travelTimeEl = (Element)i.next(); 577 metric.addTravelTime( 578 Long.valueOf(travelTimeEl.attributeValue("id1")), 579 Long.valueOf(travelTimeEl.attributeValue("id2")), 580 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 581 } 582 } 583 584 /** 585 * Load linked sections 586 * @param constraintsEl constraints element 587 * @param offeringTable offering table 588 */ 589 protected void loadLinkedSectiond(Element constraintsEl, Map<Long, Offering> offeringTable) { 590 for (Iterator<?> i = constraintsEl.elementIterator("linked-sections"); i.hasNext();) { 591 Element linkedEl = (Element) i.next(); 592 List<Section> sections = new ArrayList<Section>(); 593 for (Iterator<?> j = linkedEl.elementIterator("section"); j.hasNext();) { 594 Element sectionEl = (Element) j.next(); 595 Offering offering = offeringTable.get(Long.valueOf(sectionEl.attributeValue("offering"))); 596 sections.add(offering.getSection(Long.valueOf(sectionEl.attributeValue("id")))); 597 } 598 getModel().addLinkedSections(sections); 599 } 600 } 601 602 /** 603 * Load students 604 * @param studentsEl students element 605 * @param offeringTable offering table 606 * @param courseTable course table 607 */ 608 protected void loadStudents(Element studentsEl, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 609 List<Enrollment> bestEnrollments = new ArrayList<Enrollment>(); 610 List<Enrollment> currentEnrollments = new ArrayList<Enrollment>(); 611 for (Iterator<?> i = studentsEl.elementIterator("student"); i.hasNext();) { 612 Element studentEl = (Element) i.next(); 613 Student student = loadStudent(studentEl); 614 if (iStudentFilter != null && !iStudentFilter.accept(student)) 615 continue; 616 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 617 Element requestEl = (Element) j.next(); 618 Request request = loadRequest(requestEl, student, offeringTable, courseTable); 619 if (request == null) continue; 620 621 Element initialEl = requestEl.element("initial"); 622 if (iLoadInitial && initialEl != null) { 623 Enrollment enrollment = loadEnrollment(initialEl, request); 624 if (enrollment != null) 625 request.setInitialAssignment(enrollment); 626 } 627 Element currentEl = requestEl.element("current"); 628 if (iLoadCurrent && currentEl != null) { 629 Enrollment enrollment = loadEnrollment(currentEl, request); 630 if (enrollment != null) 631 currentEnrollments.add(enrollment); 632 } 633 Element bestEl = requestEl.element("best"); 634 if (iLoadBest && bestEl != null) { 635 Enrollment enrollment = loadEnrollment(bestEl, request); 636 if (enrollment != null) 637 bestEnrollments.add(enrollment); 638 } 639 } 640 getModel().addStudent(student); 641 } 642 643 if (!bestEnrollments.isEmpty()) { 644 // Enrollments with a reservation must go first 645 for (Enrollment enrollment : bestEnrollments) { 646 if (enrollment.getReservation() == null) continue; 647 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment); 648 if (conflicts.isEmpty()) 649 enrollment.variable().assign(0, enrollment); 650 else 651 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 652 } 653 for (Enrollment enrollment : bestEnrollments) { 654 if (enrollment.getReservation() != null) continue; 655 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment); 656 if (conflicts.isEmpty()) 657 enrollment.variable().assign(0, enrollment); 658 else 659 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 660 } 661 getModel().saveBest(); 662 } 663 664 if (!currentEnrollments.isEmpty()) { 665 for (Request request : getModel().variables()) { 666 if (request.getAssignment() != null) 667 request.unassign(0); 668 } 669 // Enrollments with a reservation must go first 670 for (Enrollment enrollment : currentEnrollments) { 671 if (enrollment.getReservation() == null) continue; 672 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment); 673 if (conflicts.isEmpty()) 674 enrollment.variable().assign(0, enrollment); 675 else 676 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 677 } 678 for (Enrollment enrollment : currentEnrollments) { 679 if (enrollment.getReservation() != null) continue; 680 Map<Constraint<Request, Enrollment>, Set<Enrollment>> conflicts = getModel().conflictConstraints(enrollment); 681 if (conflicts.isEmpty()) 682 enrollment.variable().assign(0, enrollment); 683 else 684 sLogger.warn("Enrollment " + enrollment + " conflicts with " + conflicts); 685 } 686 } 687 } 688 689 /** 690 * Load student 691 * @param studentEl student element 692 * @return loaded student 693 */ 694 protected Student loadStudent(Element studentEl) { 695 Student student = new Student(Long.parseLong(studentEl.attributeValue("id")), "true".equals(studentEl.attributeValue("dummy"))); 696 student.setExternalId(studentEl.attributeValue("externalId")); 697 student.setName(studentEl.attributeValue("name")); 698 student.setStatus(studentEl.attributeValue("status")); 699 for (Iterator<?> j = studentEl.elementIterator(); j.hasNext();) { 700 Element requestEl = (Element) j.next(); 701 if ("classification".equals(requestEl.getName())) { 702 student.getAcademicAreaClasiffications().add( 703 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"))); 704 } else if ("major".equals(requestEl.getName())) { 705 student.getMajors().add( 706 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"))); 707 } else if ("minor".equals(requestEl.getName())) { 708 student.getMinors().add( 709 new AcademicAreaCode(requestEl.attributeValue("area"), requestEl.attributeValue("code"))); 710 } 711 } 712 return student; 713 } 714 715 /** 716 * Load request 717 * @param requestEl request element 718 * @param student parent student 719 * @param offeringTable offering table 720 * @param courseTable course table 721 * @return loaded request 722 */ 723 protected Request loadRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 724 if ("freeTime".equals(requestEl.getName())) { 725 return loadFreeTime(requestEl, student); 726 } else if ("course".equals(requestEl.getName())) { 727 return loadCourseRequest(requestEl, student, offeringTable, courseTable); 728 } else { 729 return null; 730 } 731 } 732 733 /** 734 * Load free time request 735 * @param requestEl request element 736 * @param student parent student 737 * @return loaded free time request 738 */ 739 public FreeTimeRequest loadFreeTime(Element requestEl, Student student) { 740 TimeLocation time = new TimeLocation(Integer.parseInt(requestEl.attributeValue("days"), 2), 741 Integer.parseInt(requestEl.attributeValue("start")), Integer.parseInt(requestEl 742 .attributeValue("length")), 0, 0, 743 requestEl.attributeValue("datePattern") == null ? null : Long.valueOf(requestEl 744 .attributeValue("datePattern")), "", createBitSet(requestEl 745 .attributeValue("dates")), 0); 746 FreeTimeRequest request = new FreeTimeRequest(Long.parseLong(requestEl.attributeValue("id")), 747 Integer.parseInt(requestEl.attributeValue("priority")), "true".equals(requestEl 748 .attributeValue("alternative")), student, time); 749 if (requestEl.attributeValue("weight") != null) 750 request.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 751 return request; 752 } 753 754 /** 755 * Load course request 756 * @param requestEl request element 757 * @param student parent student 758 * @param offeringTable offering table 759 * @param courseTable course table 760 * @return loaded course request 761 */ 762 public CourseRequest loadCourseRequest(Element requestEl, Student student, Map<Long, Offering> offeringTable, Map<Long, Course> courseTable) { 763 List<Course> courses = new ArrayList<Course>(); 764 courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course")))); 765 for (Iterator<?> k = requestEl.elementIterator("alternative"); k.hasNext();) 766 courses.add(courseTable.get(Long.valueOf(((Element) k.next()).attributeValue("course")))); 767 Long timeStamp = null; 768 if (requestEl.attributeValue("timeStamp") != null) 769 timeStamp = Long.valueOf(requestEl.attributeValue("timeStamp")); 770 CourseRequest courseRequest = new CourseRequest( 771 Long.parseLong(requestEl.attributeValue("id")), 772 Integer.parseInt(requestEl.attributeValue("priority")), 773 "true".equals(requestEl.attributeValue("alternative")), 774 student, courses, 775 "true".equals(requestEl.attributeValue("waitlist", "false")), timeStamp); 776 if (requestEl.attributeValue("weight") != null) 777 courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight"))); 778 for (Iterator<?> k = requestEl.elementIterator("waitlisted"); k.hasNext();) { 779 Element choiceEl = (Element) k.next(); 780 courseRequest.getWaitlistedChoices().add( 781 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 782 } 783 for (Iterator<?> k = requestEl.elementIterator("selected"); k.hasNext();) { 784 Element choiceEl = (Element) k.next(); 785 courseRequest.getSelectedChoices().add( 786 new Choice(offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))), choiceEl.getText())); 787 } 788 return courseRequest; 789 } 790 791 /** 792 * Load enrollment 793 * @param enrollmentEl enrollment element (current, best, or initial) 794 * @param request parent request 795 * @return loaded enrollment 796 */ 797 protected Enrollment loadEnrollment(Element enrollmentEl, Request request) { 798 if (request instanceof CourseRequest) { 799 CourseRequest courseRequest = (CourseRequest) request; 800 HashSet<Section> sections = new HashSet<Section>(); 801 for (Iterator<?> k = enrollmentEl.elementIterator("section"); k.hasNext();) { 802 Element sectionEl = (Element) k.next(); 803 Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id"))); 804 sections.add(section); 805 } 806 Reservation reservation = null; 807 if (enrollmentEl.attributeValue("reservation", null) != null) { 808 long reservationId = Long.valueOf(enrollmentEl.attributeValue("reservation")); 809 for (Course course: courseRequest.getCourses()) 810 for (Reservation r: course.getOffering().getReservations()) 811 if (r.getId() == reservationId) { reservation = r; break; } 812 } 813 if (!sections.isEmpty()) 814 return courseRequest.createEnrollment(sections, reservation); 815 } else if (request instanceof FreeTimeRequest) { 816 return ((FreeTimeRequest)request).createEnrollment(); 817 } 818 return null; 819 } 820 821}