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