001package org.cpsolver.coursett.model; 002 003import java.util.ArrayList; 004import java.util.BitSet; 005import java.util.Collection; 006import java.util.HashSet; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Locale; 010import java.util.Map; 011import java.util.Set; 012 013import org.cpsolver.coursett.Constants; 014import org.cpsolver.coursett.constraint.ClassLimitConstraint; 015import org.cpsolver.coursett.constraint.DepartmentSpreadConstraint; 016import org.cpsolver.coursett.constraint.FlexibleConstraint; 017import org.cpsolver.coursett.constraint.GroupConstraint; 018import org.cpsolver.coursett.constraint.InstructorConstraint; 019import org.cpsolver.coursett.constraint.JenrlConstraint; 020import org.cpsolver.coursett.constraint.RoomConstraint; 021import org.cpsolver.coursett.constraint.SpreadConstraint; 022import org.cpsolver.coursett.criteria.BackToBackInstructorPreferences; 023import org.cpsolver.coursett.criteria.BrokenTimePatterns; 024import org.cpsolver.coursett.criteria.DepartmentBalancingPenalty; 025import org.cpsolver.coursett.criteria.DistributionPreferences; 026import org.cpsolver.coursett.criteria.FlexibleConstraintCriterion; 027import org.cpsolver.coursett.criteria.Perturbations; 028import org.cpsolver.coursett.criteria.RoomPreferences; 029import org.cpsolver.coursett.criteria.RoomViolations; 030import org.cpsolver.coursett.criteria.SameSubpartBalancingPenalty; 031import org.cpsolver.coursett.criteria.StudentCommittedConflict; 032import org.cpsolver.coursett.criteria.StudentConflict; 033import org.cpsolver.coursett.criteria.StudentDistanceConflict; 034import org.cpsolver.coursett.criteria.StudentHardConflict; 035import org.cpsolver.coursett.criteria.StudentOverlapConflict; 036import org.cpsolver.coursett.criteria.TimePreferences; 037import org.cpsolver.coursett.criteria.TimeViolations; 038import org.cpsolver.coursett.criteria.TooBigRooms; 039import org.cpsolver.coursett.criteria.UselessHalfHours; 040import org.cpsolver.coursett.criteria.placement.DeltaTimePreference; 041import org.cpsolver.coursett.criteria.placement.HardConflicts; 042import org.cpsolver.coursett.criteria.placement.PotentialHardConflicts; 043import org.cpsolver.coursett.criteria.placement.WeightedHardConflicts; 044import org.cpsolver.ifs.assignment.Assignment; 045import org.cpsolver.ifs.constant.ConstantModel; 046import org.cpsolver.ifs.criteria.Criterion; 047import org.cpsolver.ifs.model.Constraint; 048import org.cpsolver.ifs.model.GlobalConstraint; 049import org.cpsolver.ifs.model.WeakeningConstraint; 050import org.cpsolver.ifs.solution.Solution; 051import org.cpsolver.ifs.util.DataProperties; 052import org.cpsolver.ifs.util.DistanceMetric; 053 054 055/** 056 * Timetable model. 057 * 058 * @version CourseTT 1.3 (University Course Timetabling)<br> 059 * Copyright (C) 2006 - 2014 Tomas Muller<br> 060 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 061 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 062 * <br> 063 * This library is free software; you can redistribute it and/or modify 064 * it under the terms of the GNU Lesser General Public License as 065 * published by the Free Software Foundation; either version 3 of the 066 * License, or (at your option) any later version. <br> 067 * <br> 068 * This library is distributed in the hope that it will be useful, but 069 * WITHOUT ANY WARRANTY; without even the implied warranty of 070 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 071 * Lesser General Public License for more details. <br> 072 * <br> 073 * You should have received a copy of the GNU Lesser General Public 074 * License along with this library; if not see 075 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 076 */ 077 078public class TimetableModel extends ConstantModel<Lecture, Placement> { 079 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableModel.class); 080 private static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00", 081 new java.text.DecimalFormatSymbols(Locale.US)); 082 083 private List<InstructorConstraint> iInstructorConstraints = new ArrayList<InstructorConstraint>(); 084 private List<JenrlConstraint> iJenrlConstraints = new ArrayList<JenrlConstraint>(); 085 private List<RoomConstraint> iRoomConstraints = new ArrayList<RoomConstraint>(); 086 private List<DepartmentSpreadConstraint> iDepartmentSpreadConstraints = new ArrayList<DepartmentSpreadConstraint>(); 087 private List<SpreadConstraint> iSpreadConstraints = new ArrayList<SpreadConstraint>(); 088 private List<GroupConstraint> iGroupConstraints = new ArrayList<GroupConstraint>(); 089 private List<ClassLimitConstraint> iClassLimitConstraints = new ArrayList<ClassLimitConstraint>(); 090 private List<FlexibleConstraint> iFlexibleConstraints = new ArrayList<FlexibleConstraint>(); 091 private DataProperties iProperties = null; 092 private int iYear = -1; 093 private List<BitSet> iWeeks = null; 094 095 private HashSet<Student> iAllStudents = new HashSet<Student>(); 096 097 private DistanceMetric iDistanceMetric = null; 098 099 private StudentSectioning iStudentSectioning = null; 100 101 public TimetableModel(DataProperties properties) { 102 super(); 103 iProperties = properties; 104 iDistanceMetric = new DistanceMetric(properties); 105 if (properties.getPropertyBoolean("OnFlySectioning.Enabled", false)) 106 addModelListener(new OnFlySectioning(this)); 107 String criteria = properties.getProperty("General.Criteria", 108 // Objectives 109 StudentConflict.class.getName() + ";" + 110 StudentDistanceConflict.class.getName() + ";" + 111 StudentHardConflict.class.getName() + ";" + 112 StudentCommittedConflict.class.getName() + ";" + 113 StudentOverlapConflict.class.getName() + ";" + 114 UselessHalfHours.class.getName() + ";" + 115 BrokenTimePatterns.class.getName() + ";" + 116 TooBigRooms.class.getName() + ";" + 117 TimePreferences.class.getName() + ";" + 118 RoomPreferences.class.getName() + ";" + 119 DistributionPreferences.class.getName() + ";" + 120 SameSubpartBalancingPenalty.class.getName() + ";" + 121 DepartmentBalancingPenalty.class.getName() + ";" + 122 BackToBackInstructorPreferences.class.getName() + ";" + 123 Perturbations.class.getName() + ";" + 124 // Additional placement selection criteria 125 // AssignmentCount.class.getName() + ";" + 126 DeltaTimePreference.class.getName() + ";" + 127 HardConflicts.class.getName() + ";" + 128 PotentialHardConflicts.class.getName() + ";" + 129 FlexibleConstraintCriterion.class.getName() + ";" + 130 WeightedHardConflicts.class.getName()); 131 // Interactive mode -- count time / room violations 132 if (properties.getPropertyBoolean("General.InteractiveMode", false)) 133 criteria += ";" + TimeViolations.class.getName() + ";" + RoomViolations.class.getName(); 134 // Additional (custom) criteria 135 criteria += ";" + properties.getProperty("General.AdditionalCriteria", ""); 136 for (String criterion: criteria.split("\\;")) { 137 if (criterion == null || criterion.isEmpty()) continue; 138 try { 139 @SuppressWarnings("unchecked") 140 Class<Criterion<Lecture, Placement>> clazz = (Class<Criterion<Lecture, Placement>>)Class.forName(criterion); 141 addCriterion(clazz.newInstance()); 142 } catch (Exception e) { 143 sLogger.error("Unable to use " + criterion + ": " + e.getMessage()); 144 } 145 } 146 try { 147 String studentSectioningClassName = properties.getProperty("StudentSectioning.Class", DefaultStudentSectioning.class.getName()); 148 Class<?> studentSectioningClass = Class.forName(studentSectioningClassName); 149 iStudentSectioning = (StudentSectioning)studentSectioningClass.getConstructor(TimetableModel.class).newInstance(this); 150 } catch (Exception e) { 151 sLogger.error("Failed to load custom student sectioning class: " + e.getMessage()); 152 iStudentSectioning = new DefaultStudentSectioning(this); 153 } 154 } 155 156 public DistanceMetric getDistanceMetric() { 157 return iDistanceMetric; 158 } 159 160 /** 161 * Returns interface to the student sectioning functions needed during course timetabling. 162 * Defaults to an instance of {@link DefaultStudentSectioning}, can be changed using the StudentSectioning.Class parameter. 163 * @return student sectioning 164 */ 165 public StudentSectioning getStudentSectioning() { 166 return iStudentSectioning; 167 } 168 169 public DataProperties getProperties() { 170 return iProperties; 171 } 172 173 /** 174 * Student final sectioning (switching students between sections of the same 175 * class in order to minimize overall number of student conflicts) 176 * @param assignment current assignment 177 */ 178 public void switchStudents(Assignment<Lecture, Placement> assignment) { 179 getStudentSectioning().switchStudents(new Solution<Lecture, Placement>(this, assignment)); 180 } 181 182 public Map<String, String> getBounds(Assignment<Lecture, Placement> assignment) { 183 Map<String, String> ret = new HashMap<String, String>(); 184 ret.put("Room preferences min", "" + getCriterion(RoomPreferences.class).getBounds(assignment)[0]); 185 ret.put("Room preferences max", "" + getCriterion(RoomPreferences.class).getBounds(assignment)[1]); 186 ret.put("Time preferences min", "" + getCriterion(TimePreferences.class).getBounds(assignment)[0]); 187 ret.put("Time preferences max", "" + getCriterion(TimePreferences.class).getBounds(assignment)[1]); 188 ret.put("Distribution preferences min", "" + getCriterion(DistributionPreferences.class).getBounds(assignment)[0]); 189 ret.put("Distribution preferences max", "" + getCriterion(DistributionPreferences.class).getBounds(assignment)[1]); 190 if (getProperties().getPropertyBoolean("General.UseDistanceConstraints", false)) { 191 ret.put("Back-to-back instructor preferences max", "" + getCriterion(BackToBackInstructorPreferences.class).getBounds(assignment)[1]); 192 } 193 ret.put("Too big rooms max", "" + getCriterion(TooBigRooms.class).getBounds(assignment)[0]); 194 ret.put("Useless half-hours", "" + getCriterion(UselessHalfHours.class).getBounds(assignment)[0]); 195 return ret; 196 } 197 198 /** Global info */ 199 @Override 200 public Map<String, String> getInfo(Assignment<Lecture, Placement> assignment) { 201 Map<String, String> ret = super.getInfo(assignment); 202 ret.put("Memory usage", getMem()); 203 204 Criterion<Lecture, Placement> rp = getCriterion(RoomPreferences.class); 205 Criterion<Lecture, Placement> rv = getCriterion(RoomViolations.class); 206 ret.put("Room preferences", getPerc(rp.getValue(assignment), rp.getBounds(assignment)[0], rp.getBounds(assignment)[1]) + "% (" + Math.round(rp.getValue(assignment)) + ")" 207 + (rv != null && rv.getValue(assignment) >= 0.5 ? " [hard:" + Math.round(rv.getValue(assignment)) + "]" : "")); 208 209 Criterion<Lecture, Placement> tp = getCriterion(TimePreferences.class); 210 Criterion<Lecture, Placement> tv = getCriterion(TimeViolations.class); 211 ret.put("Time preferences", getPerc(tp.getValue(assignment), tp.getBounds(assignment)[0], tp.getBounds(assignment)[1]) + "% (" + sDoubleFormat.format(tp.getValue(assignment)) + ")" 212 + (tv != null && tv.getValue(assignment) >= 0.5 ? " [hard:" + Math.round(tv.getValue(assignment)) + "]" : "")); 213 214 Criterion<Lecture, Placement> dp = getCriterion(DistributionPreferences.class); 215 ret.put("Distribution preferences", getPerc(dp.getValue(assignment), dp.getBounds(assignment)[0], dp.getBounds(assignment)[1]) + "% (" + sDoubleFormat.format(dp.getValue(assignment)) + ")"); 216 217 Criterion<Lecture, Placement> sc = getCriterion(StudentConflict.class); 218 Criterion<Lecture, Placement> shc = getCriterion(StudentHardConflict.class); 219 Criterion<Lecture, Placement> sdc = getCriterion(StudentDistanceConflict.class); 220 Criterion<Lecture, Placement> scc = getCriterion(StudentCommittedConflict.class); 221 ret.put("Student conflicts", Math.round(scc.getValue(assignment) + sc.getValue(assignment)) + 222 " [committed:" + Math.round(scc.getValue(assignment)) + 223 ", distance:" + Math.round(sdc.getValue(assignment)) + 224 ", hard:" + Math.round(shc.getValue(assignment)) + "]"); 225 226 if (!getSpreadConstraints().isEmpty()) { 227 Criterion<Lecture, Placement> ip = getCriterion(BackToBackInstructorPreferences.class); 228 ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(assignment), ip.getBounds(assignment)[0], ip.getBounds(assignment)[1]) + "% (" + Math.round(ip.getValue(assignment)) + ")"); 229 } 230 231 if (!getDepartmentSpreadConstraints().isEmpty()) { 232 Criterion<Lecture, Placement> dbp = getCriterion(DepartmentBalancingPenalty.class); 233 ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue(assignment))); 234 } 235 236 Criterion<Lecture, Placement> sbp = getCriterion(SameSubpartBalancingPenalty.class); 237 ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue(assignment))); 238 239 Criterion<Lecture, Placement> tbr = getCriterion(TooBigRooms.class); 240 ret.put("Too big rooms", getPercRev(tbr.getValue(assignment), tbr.getBounds(assignment)[1], tbr.getBounds(assignment)[0]) + "% (" + Math.round(tbr.getValue(assignment)) + ")"); 241 242 Criterion<Lecture, Placement> uh = getCriterion(UselessHalfHours.class); 243 Criterion<Lecture, Placement> bt = getCriterion(BrokenTimePatterns.class); 244 245 ret.put("Useless half-hours", getPercRev(uh.getValue(assignment) + bt.getValue(assignment), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds(assignment)[0]) + 246 "% (" + Math.round(uh.getValue(assignment)) + " + " + Math.round(bt.getValue(assignment)) + ")"); 247 return ret; 248 } 249 250 @Override 251 public Map<String, String> getInfo(Assignment<Lecture, Placement> assignment, Collection<Lecture> variables) { 252 Map<String, String> ret = super.getInfo(assignment, variables); 253 254 ret.put("Memory usage", getMem()); 255 256 Criterion<Lecture, Placement> rp = getCriterion(RoomPreferences.class); 257 ret.put("Room preferences", getPerc(rp.getValue(assignment, variables), rp.getBounds(assignment, variables)[0], rp.getBounds(assignment, variables)[1]) + "% (" + Math.round(rp.getValue(assignment, variables)) + ")"); 258 259 Criterion<Lecture, Placement> tp = getCriterion(TimePreferences.class); 260 ret.put("Time preferences", getPerc(tp.getValue(assignment, variables), tp.getBounds(assignment, variables)[0], tp.getBounds(assignment, variables)[1]) + "% (" + sDoubleFormat.format(tp.getValue(assignment, variables)) + ")"); 261 262 Criterion<Lecture, Placement> dp = getCriterion(DistributionPreferences.class); 263 ret.put("Distribution preferences", getPerc(dp.getValue(assignment, variables), dp.getBounds(assignment, variables)[0], dp.getBounds(assignment, variables)[1]) + "% (" + sDoubleFormat.format(dp.getValue(assignment, variables)) + ")"); 264 265 Criterion<Lecture, Placement> sc = getCriterion(StudentConflict.class); 266 Criterion<Lecture, Placement> shc = getCriterion(StudentHardConflict.class); 267 Criterion<Lecture, Placement> sdc = getCriterion(StudentDistanceConflict.class); 268 Criterion<Lecture, Placement> scc = getCriterion(StudentCommittedConflict.class); 269 ret.put("Student conflicts", Math.round(scc.getValue(assignment, variables) + sc.getValue(assignment, variables)) + 270 " [committed:" + Math.round(scc.getValue(assignment, variables)) + 271 ", distance:" + Math.round(sdc.getValue(assignment, variables)) + 272 ", hard:" + Math.round(shc.getValue(assignment, variables)) + "]"); 273 274 if (!getSpreadConstraints().isEmpty()) { 275 Criterion<Lecture, Placement> ip = getCriterion(BackToBackInstructorPreferences.class); 276 ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(assignment, variables), ip.getBounds(assignment, variables)[0], ip.getBounds(assignment, variables)[1]) + "% (" + Math.round(ip.getValue(assignment, variables)) + ")"); 277 } 278 279 if (!getDepartmentSpreadConstraints().isEmpty()) { 280 Criterion<Lecture, Placement> dbp = getCriterion(DepartmentBalancingPenalty.class); 281 ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue(assignment, variables))); 282 } 283 284 Criterion<Lecture, Placement> sbp = getCriterion(SameSubpartBalancingPenalty.class); 285 ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue(assignment, variables))); 286 287 Criterion<Lecture, Placement> tbr = getCriterion(TooBigRooms.class); 288 ret.put("Too big rooms", getPercRev(tbr.getValue(assignment, variables), tbr.getBounds(assignment, variables)[1], tbr.getBounds(assignment, variables)[0]) + "% (" + Math.round(tbr.getValue(assignment, variables)) + ")"); 289 290 Criterion<Lecture, Placement> uh = getCriterion(UselessHalfHours.class); 291 Criterion<Lecture, Placement> bt = getCriterion(BrokenTimePatterns.class); 292 293 ret.put("Useless half-hours", getPercRev(uh.getValue(assignment, variables) + bt.getValue(assignment, variables), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds(assignment, variables)[0]) + 294 "% (" + Math.round(uh.getValue(assignment, variables)) + " + " + Math.round(bt.getValue(assignment, variables)) + ")"); 295 return ret; 296 } 297 298 @Override 299 public void addConstraint(Constraint<Lecture, Placement> constraint) { 300 super.addConstraint(constraint); 301 if (constraint instanceof InstructorConstraint) { 302 iInstructorConstraints.add((InstructorConstraint) constraint); 303 } else if (constraint instanceof JenrlConstraint) { 304 iJenrlConstraints.add((JenrlConstraint) constraint); 305 } else if (constraint instanceof RoomConstraint) { 306 iRoomConstraints.add((RoomConstraint) constraint); 307 } else if (constraint instanceof DepartmentSpreadConstraint) { 308 iDepartmentSpreadConstraints.add((DepartmentSpreadConstraint) constraint); 309 } else if (constraint instanceof SpreadConstraint) { 310 iSpreadConstraints.add((SpreadConstraint) constraint); 311 } else if (constraint instanceof ClassLimitConstraint) { 312 iClassLimitConstraints.add((ClassLimitConstraint) constraint); 313 } else if (constraint instanceof GroupConstraint) { 314 iGroupConstraints.add((GroupConstraint) constraint); 315 } else if (constraint instanceof FlexibleConstraint) { 316 iFlexibleConstraints.add((FlexibleConstraint) constraint); 317 } 318 } 319 320 @Override 321 public void removeConstraint(Constraint<Lecture, Placement> constraint) { 322 super.removeConstraint(constraint); 323 if (constraint instanceof InstructorConstraint) { 324 iInstructorConstraints.remove(constraint); 325 } else if (constraint instanceof JenrlConstraint) { 326 iJenrlConstraints.remove(constraint); 327 } else if (constraint instanceof RoomConstraint) { 328 iRoomConstraints.remove(constraint); 329 } else if (constraint instanceof DepartmentSpreadConstraint) { 330 iDepartmentSpreadConstraints.remove(constraint); 331 } else if (constraint instanceof SpreadConstraint) { 332 iSpreadConstraints.remove(constraint); 333 } else if (constraint instanceof ClassLimitConstraint) { 334 iClassLimitConstraints.remove(constraint); 335 } else if (constraint instanceof GroupConstraint) { 336 iGroupConstraints.remove(constraint); 337 } else if (constraint instanceof FlexibleConstraint) { 338 iFlexibleConstraints.remove(constraint); 339 } 340 } 341 342 /** The list of all instructor constraints 343 * @return list of instructor constraints 344 **/ 345 public List<InstructorConstraint> getInstructorConstraints() { 346 return iInstructorConstraints; 347 } 348 349 /** The list of all group constraints 350 * @return list of group (distribution) constraints 351 **/ 352 public List<GroupConstraint> getGroupConstraints() { 353 return iGroupConstraints; 354 } 355 356 /** The list of all jenrl constraints 357 * @return list of join enrollment constraints 358 **/ 359 public List<JenrlConstraint> getJenrlConstraints() { 360 return iJenrlConstraints; 361 } 362 363 /** The list of all room constraints 364 * @return list of room constraints 365 **/ 366 public List<RoomConstraint> getRoomConstraints() { 367 return iRoomConstraints; 368 } 369 370 /** The list of all departmental spread constraints 371 * @return list of department spread constraints 372 **/ 373 public List<DepartmentSpreadConstraint> getDepartmentSpreadConstraints() { 374 return iDepartmentSpreadConstraints; 375 } 376 377 public List<SpreadConstraint> getSpreadConstraints() { 378 return iSpreadConstraints; 379 } 380 381 public List<ClassLimitConstraint> getClassLimitConstraints() { 382 return iClassLimitConstraints; 383 } 384 385 public List<FlexibleConstraint> getFlexibleConstraints() { 386 return iFlexibleConstraints; 387 } 388 389 @Override 390 public double getTotalValue(Assignment<Lecture, Placement> assignment) { 391 double ret = 0; 392 for (Criterion<Lecture, Placement> criterion: getCriteria()) 393 ret += criterion.getWeightedValue(assignment); 394 return ret; 395 } 396 397 @Override 398 public double getTotalValue(Assignment<Lecture, Placement> assignment, Collection<Lecture> variables) { 399 double ret = 0; 400 for (Criterion<Lecture, Placement> criterion: getCriteria()) 401 ret += criterion.getWeightedValue(assignment, variables); 402 return ret; 403 } 404 405 public int getYear() { 406 return iYear; 407 } 408 409 public void setYear(int year) { 410 iYear = year; 411 } 412 413 public Set<Student> getAllStudents() { 414 return iAllStudents; 415 } 416 417 public void addStudent(Student student) { 418 iAllStudents.add(student); 419 } 420 421 public void removeStudent(Student student) { 422 iAllStudents.remove(student); 423 } 424 425 /** 426 * Returns amount of allocated memory. 427 * 428 * @return amount of allocated memory to be written in the log 429 */ 430 public static synchronized String getMem() { 431 Runtime rt = Runtime.getRuntime(); 432 return sDoubleFormat.format(((double) (rt.totalMemory() - rt.freeMemory())) / 1048576) + "M"; 433 } 434 435 436 /** 437 * Returns the set of conflicting variables with this value, if it is 438 * assigned to its variable. Conflicts with constraints that implement 439 * {@link WeakeningConstraint} are ignored. 440 * @param assignment current assignment 441 * @param value placement that is being considered 442 * @return computed conflicting assignments 443 */ 444 public Set<Placement> conflictValuesSkipWeakeningConstraints(Assignment<Lecture, Placement> assignment, Placement value) { 445 Set<Placement> conflictValues = new HashSet<Placement>(); 446 for (Constraint<Lecture, Placement> constraint : value.variable().hardConstraints()) { 447 if (constraint instanceof WeakeningConstraint) continue; 448 if (constraint instanceof GroupConstraint) 449 ((GroupConstraint)constraint).computeConflictsNoForwardCheck(assignment, value, conflictValues); 450 else 451 constraint.computeConflicts(assignment, value, conflictValues); 452 } 453 for (GlobalConstraint<Lecture, Placement> constraint : globalConstraints()) { 454 if (constraint instanceof WeakeningConstraint) continue; 455 constraint.computeConflicts(assignment, value, conflictValues); 456 } 457 return conflictValues; 458 } 459 460 /** 461 * The method creates date patterns (bitsets) which represent the weeks of a 462 * semester. 463 * 464 * @return a list of BitSets which represents the weeks of a semester. 465 */ 466 public List<BitSet> getWeeks() { 467 if (iWeeks == null) { 468 String defaultDatePattern = getProperties().getProperty("DatePattern.CustomDatePattern", null); 469 if (defaultDatePattern == null){ 470 defaultDatePattern = getProperties().getProperty("DatePattern.Default"); 471 } 472 BitSet fullTerm = null; 473 if (defaultDatePattern == null) { 474 // Take the date pattern that is being used most often 475 Map<Long, Integer> counter = new HashMap<Long, Integer>(); 476 int max = 0; String name = null; Long id = null; 477 for (Lecture lecture: variables()) { 478 if (lecture.isCommitted()) continue; 479 for (TimeLocation time: lecture.timeLocations()) { 480 if (time.getWeekCode() != null && time.getDatePatternId() != null) { 481 int count = 1; 482 if (counter.containsKey(time.getDatePatternId())) 483 count += counter.get(time.getDatePatternId()); 484 counter.put(time.getDatePatternId(), count); 485 if (count > max) { 486 max = count; fullTerm = time.getWeekCode(); name = time.getDatePatternName(); id = time.getDatePatternId(); 487 } 488 } 489 } 490 } 491 sLogger.info("Using date pattern " + name + " (id " + id + ") as the default."); 492 } else { 493 // Create default date pattern 494 fullTerm = new BitSet(defaultDatePattern.length()); 495 for (int i = 0; i < defaultDatePattern.length(); i++) { 496 if (defaultDatePattern.charAt(i) == 49) { 497 fullTerm.set(i); 498 } 499 } 500 } 501 502 if (fullTerm == null) return null; 503 504 iWeeks = new ArrayList<BitSet>(); 505 if (getProperties().getPropertyBoolean("DatePattern.ShiftWeeks", false)) { 506 // Cut date pattern into weeks (each week takes 7 consecutive bits, starting on the next positive bit) 507 for (int i = fullTerm.nextSetBit(0); i < fullTerm.length(); ) { 508 if (!fullTerm.get(i)) { 509 i++; continue; 510 } 511 BitSet w = new BitSet(i + 7); 512 for (int j = 0; j < 7; j++) 513 if (fullTerm.get(i + j)) w.set(i + j); 514 iWeeks.add(w); 515 i += 7; 516 } 517 } else { 518 // Cut date pattern into weeks (each week takes 7 consecutive bits starting on the first bit of the default date pattern, no pauses between weeks) 519 for (int i = fullTerm.nextSetBit(0); i < fullTerm.length(); ) { 520 BitSet w = new BitSet(i + 7); 521 for (int j = 0; j < 7; j++) 522 if (fullTerm.get(i + j)) w.set(i + j); 523 iWeeks.add(w); 524 i += 7; 525 } 526 } 527 } 528 return iWeeks; 529 } 530}