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