001package net.sf.cpsolver.coursett.model; 002 003import java.util.ArrayList; 004import java.util.BitSet; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.Comparator; 008import java.util.HashSet; 009import java.util.HashMap; 010import java.util.List; 011import java.util.Locale; 012import java.util.Map; 013import java.util.Set; 014 015import net.sf.cpsolver.coursett.Constants; 016import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint; 017import net.sf.cpsolver.coursett.constraint.DepartmentSpreadConstraint; 018import net.sf.cpsolver.coursett.constraint.GroupConstraint; 019import net.sf.cpsolver.coursett.constraint.InstructorConstraint; 020import net.sf.cpsolver.coursett.constraint.JenrlConstraint; 021import net.sf.cpsolver.coursett.constraint.FlexibleConstraint; 022import net.sf.cpsolver.coursett.constraint.RoomConstraint; 023import net.sf.cpsolver.coursett.constraint.SpreadConstraint; 024import net.sf.cpsolver.coursett.criteria.BackToBackInstructorPreferences; 025import net.sf.cpsolver.coursett.criteria.BrokenTimePatterns; 026import net.sf.cpsolver.coursett.criteria.DepartmentBalancingPenalty; 027import net.sf.cpsolver.coursett.criteria.DistributionPreferences; 028import net.sf.cpsolver.coursett.criteria.FlexibleConstraintCriterion; 029import net.sf.cpsolver.coursett.criteria.Perturbations; 030import net.sf.cpsolver.coursett.criteria.RoomPreferences; 031import net.sf.cpsolver.coursett.criteria.RoomViolations; 032import net.sf.cpsolver.coursett.criteria.SameSubpartBalancingPenalty; 033import net.sf.cpsolver.coursett.criteria.StudentCommittedConflict; 034import net.sf.cpsolver.coursett.criteria.StudentConflict; 035import net.sf.cpsolver.coursett.criteria.StudentDistanceConflict; 036import net.sf.cpsolver.coursett.criteria.StudentHardConflict; 037import net.sf.cpsolver.coursett.criteria.StudentOverlapConflict; 038import net.sf.cpsolver.coursett.criteria.TimePreferences; 039import net.sf.cpsolver.coursett.criteria.TimeViolations; 040import net.sf.cpsolver.coursett.criteria.TooBigRooms; 041import net.sf.cpsolver.coursett.criteria.UselessHalfHours; 042import net.sf.cpsolver.coursett.criteria.placement.AssignmentCount; 043import net.sf.cpsolver.coursett.criteria.placement.DeltaTimePreference; 044import net.sf.cpsolver.coursett.criteria.placement.HardConflicts; 045import net.sf.cpsolver.coursett.criteria.placement.PotentialHardConflicts; 046import net.sf.cpsolver.coursett.criteria.placement.WeightedHardConflicts; 047import net.sf.cpsolver.ifs.constant.ConstantModel; 048import net.sf.cpsolver.ifs.criteria.Criterion; 049import net.sf.cpsolver.ifs.model.Constraint; 050import net.sf.cpsolver.ifs.model.GlobalConstraint; 051import net.sf.cpsolver.ifs.model.WeakeningConstraint; 052import net.sf.cpsolver.ifs.util.DataProperties; 053import net.sf.cpsolver.ifs.util.DistanceMetric; 054 055/** 056 * Timetable model. 057 * 058 * @version CourseTT 1.2 (University Course Timetabling)<br> 059 * Copyright (C) 2006 - 2010 Tomáš Müller<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 */ 164 public StudentSectioning getStudentSectioning() { 165 return iStudentSectioning; 166 } 167 168 public DataProperties getProperties() { 169 return iProperties; 170 } 171 172 /** 173 * Student final sectioning (switching students between sections of the same 174 * class in order to minimize overall number of student conflicts) 175 */ 176 public void switchStudents() { 177 getStudentSectioning().switchStudents(this); 178 } 179 180 /** 181 * String representation -- returns a list of values of objective criteria 182 */ 183 @Override 184 public String toString() { 185 List<Criterion<Lecture, Placement>> criteria = new ArrayList<Criterion<Lecture,Placement>>(getCriteria()); 186 Collections.sort(criteria, new Comparator<Criterion<Lecture, Placement>>() { 187 @Override 188 public int compare(Criterion<Lecture, Placement> c1, Criterion<Lecture, Placement> c2) { 189 int cmp = -Double.compare(c1.getWeight(), c2.getWeight()); 190 if (cmp != 0) return cmp; 191 return c1.getName().compareTo(c2.getName()); 192 } 193 }); 194 String ret = ""; 195 for (Criterion<Lecture, Placement> criterion: criteria) { 196 String val = criterion.toString(); 197 if (!val.isEmpty()) 198 ret += ", " + val; 199 } 200 return (nrUnassignedVariables() == 0 ? "" : "V:" + nrAssignedVariables() + "/" + variables().size() + ", ") + 201 "T:" + sDoubleFormat.format(getTotalValue()) + ret; 202 } 203 204 public Map<String, String> getBounds() { 205 Map<String, String> ret = new HashMap<String, String>(); 206 ret.put("Room preferences min", "" + getCriterion(RoomPreferences.class).getBounds()[0]); 207 ret.put("Room preferences max", "" + getCriterion(RoomPreferences.class).getBounds()[1]); 208 ret.put("Time preferences min", "" + getCriterion(TimePreferences.class).getBounds()[0]); 209 ret.put("Time preferences max", "" + getCriterion(TimePreferences.class).getBounds()[1]); 210 ret.put("Distribution preferences min", "" + getCriterion(DistributionPreferences.class).getBounds()[0]); 211 ret.put("Distribution preferences max", "" + getCriterion(DistributionPreferences.class).getBounds()[1]); 212 if (getProperties().getPropertyBoolean("General.UseDistanceConstraints", false)) { 213 ret.put("Back-to-back instructor preferences max", "" + getCriterion(BackToBackInstructorPreferences.class).getBounds()[1]); 214 } 215 ret.put("Too big rooms max", "" + getCriterion(TooBigRooms.class).getBounds()[0]); 216 ret.put("Useless half-hours", "" + getCriterion(UselessHalfHours.class).getBounds()[0]); 217 return ret; 218 } 219 220 /** Global info */ 221 @Override 222 public Map<String, String> getInfo() { 223 Map<String, String> ret = super.getInfo(); 224 ret.put("Memory usage", getMem()); 225 226 Criterion<Lecture, Placement> rp = getCriterion(RoomPreferences.class); 227 Criterion<Lecture, Placement> rv = getCriterion(RoomViolations.class); 228 ret.put("Room preferences", getPerc(rp.getValue(), rp.getBounds()[0], rp.getBounds()[1]) + "% (" + Math.round(rp.getValue()) + ")" 229 + (rv != null && rv.getValue() >= 0.5 ? " [hard:" + Math.round(rv.getValue()) + "]" : "")); 230 231 Criterion<Lecture, Placement> tp = getCriterion(TimePreferences.class); 232 Criterion<Lecture, Placement> tv = getCriterion(TimeViolations.class); 233 ret.put("Time preferences", getPerc(tp.getValue(), tp.getBounds()[0], tp.getBounds()[1]) + "% (" + sDoubleFormat.format(tp.getValue()) + ")" 234 + (tv != null && tv.getValue() >= 0.5 ? " [hard:" + Math.round(tv.getValue()) + "]" : "")); 235 236 Criterion<Lecture, Placement> dp = getCriterion(DistributionPreferences.class); 237 ret.put("Distribution preferences", getPerc(dp.getValue(), dp.getBounds()[0], dp.getBounds()[1]) + "% (" + sDoubleFormat.format(dp.getValue()) + ")"); 238 239 Criterion<Lecture, Placement> sc = getCriterion(StudentConflict.class); 240 Criterion<Lecture, Placement> shc = getCriterion(StudentHardConflict.class); 241 Criterion<Lecture, Placement> sdc = getCriterion(StudentDistanceConflict.class); 242 Criterion<Lecture, Placement> scc = getCriterion(StudentCommittedConflict.class); 243 ret.put("Student conflicts", Math.round(scc.getValue() + sc.getValue()) + 244 " [committed:" + Math.round(scc.getValue()) + 245 ", distance:" + Math.round(sdc.getValue()) + 246 ", hard:" + Math.round(shc.getValue()) + "]"); 247 248 if (!getSpreadConstraints().isEmpty()) { 249 Criterion<Lecture, Placement> ip = getCriterion(BackToBackInstructorPreferences.class); 250 ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(), ip.getBounds()[0], ip.getBounds()[1]) + "% (" + Math.round(ip.getValue()) + ")"); 251 } 252 253 if (!getDepartmentSpreadConstraints().isEmpty()) { 254 Criterion<Lecture, Placement> dbp = getCriterion(DepartmentBalancingPenalty.class); 255 ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue())); 256 } 257 258 Criterion<Lecture, Placement> sbp = getCriterion(SameSubpartBalancingPenalty.class); 259 ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue())); 260 261 Criterion<Lecture, Placement> tbr = getCriterion(TooBigRooms.class); 262 ret.put("Too big rooms", getPercRev(tbr.getValue(), tbr.getBounds()[1], tbr.getBounds()[0]) + "% (" + Math.round(tbr.getValue()) + ")"); 263 264 Criterion<Lecture, Placement> uh = getCriterion(UselessHalfHours.class); 265 Criterion<Lecture, Placement> bt = getCriterion(BrokenTimePatterns.class); 266 267 ret.put("Useless half-hours", getPercRev(uh.getValue() + bt.getValue(), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds()[0]) + 268 "% (" + Math.round(uh.getValue()) + " + " + Math.round(bt.getValue()) + ")"); 269 return ret; 270 } 271 272 @Override 273 public Map<String, String> getInfo(Collection<Lecture> variables) { 274 Map<String, String> ret = super.getInfo(variables); 275 276 ret.put("Memory usage", getMem()); 277 278 Criterion<Lecture, Placement> rp = getCriterion(RoomPreferences.class); 279 ret.put("Room preferences", getPerc(rp.getValue(variables), rp.getBounds(variables)[0], rp.getBounds(variables)[1]) + "% (" + Math.round(rp.getValue(variables)) + ")"); 280 281 Criterion<Lecture, Placement> tp = getCriterion(TimePreferences.class); 282 ret.put("Time preferences", getPerc(tp.getValue(variables), tp.getBounds(variables)[0], tp.getBounds(variables)[1]) + "% (" + sDoubleFormat.format(tp.getValue(variables)) + ")"); 283 284 Criterion<Lecture, Placement> dp = getCriterion(DistributionPreferences.class); 285 ret.put("Distribution preferences", getPerc(dp.getValue(variables), dp.getBounds(variables)[0], dp.getBounds(variables)[1]) + "% (" + sDoubleFormat.format(dp.getValue(variables)) + ")"); 286 287 Criterion<Lecture, Placement> sc = getCriterion(StudentConflict.class); 288 Criterion<Lecture, Placement> shc = getCriterion(StudentHardConflict.class); 289 Criterion<Lecture, Placement> sdc = getCriterion(StudentDistanceConflict.class); 290 Criterion<Lecture, Placement> scc = getCriterion(StudentCommittedConflict.class); 291 ret.put("Student conflicts", Math.round(scc.getValue(variables) + sc.getValue(variables)) + 292 " [committed:" + Math.round(scc.getValue(variables)) + 293 ", distance:" + Math.round(sdc.getValue(variables)) + 294 ", hard:" + Math.round(shc.getValue(variables)) + "]"); 295 296 if (!getSpreadConstraints().isEmpty()) { 297 Criterion<Lecture, Placement> ip = getCriterion(BackToBackInstructorPreferences.class); 298 ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(variables), ip.getBounds(variables)[0], ip.getBounds(variables)[1]) + "% (" + Math.round(ip.getValue(variables)) + ")"); 299 } 300 301 if (!getDepartmentSpreadConstraints().isEmpty()) { 302 Criterion<Lecture, Placement> dbp = getCriterion(DepartmentBalancingPenalty.class); 303 ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue(variables))); 304 } 305 306 Criterion<Lecture, Placement> sbp = getCriterion(SameSubpartBalancingPenalty.class); 307 ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue(variables))); 308 309 Criterion<Lecture, Placement> tbr = getCriterion(TooBigRooms.class); 310 ret.put("Too big rooms", getPercRev(tbr.getValue(variables), tbr.getBounds(variables)[1], tbr.getBounds(variables)[0]) + "% (" + Math.round(tbr.getValue(variables)) + ")"); 311 312 Criterion<Lecture, Placement> uh = getCriterion(UselessHalfHours.class); 313 Criterion<Lecture, Placement> bt = getCriterion(BrokenTimePatterns.class); 314 315 ret.put("Useless half-hours", getPercRev(uh.getValue(variables) + bt.getValue(variables), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds(variables)[0]) + 316 "% (" + Math.round(uh.getValue(variables)) + " + " + Math.round(bt.getValue(variables)) + ")"); 317 return ret; 318 } 319 320 @Override 321 public void addConstraint(Constraint<Lecture, Placement> constraint) { 322 super.addConstraint(constraint); 323 if (constraint instanceof InstructorConstraint) { 324 iInstructorConstraints.add((InstructorConstraint) constraint); 325 } else if (constraint instanceof JenrlConstraint) { 326 iJenrlConstraints.add((JenrlConstraint) constraint); 327 } else if (constraint instanceof RoomConstraint) { 328 iRoomConstraints.add((RoomConstraint) constraint); 329 } else if (constraint instanceof DepartmentSpreadConstraint) { 330 iDepartmentSpreadConstraints.add((DepartmentSpreadConstraint) constraint); 331 } else if (constraint instanceof SpreadConstraint) { 332 iSpreadConstraints.add((SpreadConstraint) constraint); 333 } else if (constraint instanceof ClassLimitConstraint) { 334 iClassLimitConstraints.add((ClassLimitConstraint) constraint); 335 } else if (constraint instanceof GroupConstraint) { 336 iGroupConstraints.add((GroupConstraint) constraint); 337 } else if (constraint instanceof FlexibleConstraint) { 338 iFlexibleConstraints.add((FlexibleConstraint) constraint); 339 } 340 } 341 342 @Override 343 public void removeConstraint(Constraint<Lecture, Placement> constraint) { 344 super.removeConstraint(constraint); 345 if (constraint instanceof InstructorConstraint) { 346 iInstructorConstraints.remove(constraint); 347 } else if (constraint instanceof JenrlConstraint) { 348 iJenrlConstraints.remove(constraint); 349 } else if (constraint instanceof RoomConstraint) { 350 iRoomConstraints.remove(constraint); 351 } else if (constraint instanceof DepartmentSpreadConstraint) { 352 iDepartmentSpreadConstraints.remove(constraint); 353 } else if (constraint instanceof SpreadConstraint) { 354 iSpreadConstraints.remove(constraint); 355 } else if (constraint instanceof ClassLimitConstraint) { 356 iClassLimitConstraints.remove(constraint); 357 } else if (constraint instanceof GroupConstraint) { 358 iGroupConstraints.remove(constraint); 359 } else if (constraint instanceof FlexibleConstraint) { 360 iFlexibleConstraints.remove(constraint); 361 } 362 } 363 364 /** The list of all instructor constraints */ 365 public List<InstructorConstraint> getInstructorConstraints() { 366 return iInstructorConstraints; 367 } 368 369 /** The list of all group constraints */ 370 public List<GroupConstraint> getGroupConstraints() { 371 return iGroupConstraints; 372 } 373 374 /** The list of all jenrl constraints */ 375 public List<JenrlConstraint> getJenrlConstraints() { 376 return iJenrlConstraints; 377 } 378 379 /** The list of all room constraints */ 380 public List<RoomConstraint> getRoomConstraints() { 381 return iRoomConstraints; 382 } 383 384 /** The list of all departmental spread constraints */ 385 public List<DepartmentSpreadConstraint> getDepartmentSpreadConstraints() { 386 return iDepartmentSpreadConstraints; 387 } 388 389 public List<SpreadConstraint> getSpreadConstraints() { 390 return iSpreadConstraints; 391 } 392 393 public List<ClassLimitConstraint> getClassLimitConstraints() { 394 return iClassLimitConstraints; 395 } 396 397 public List<FlexibleConstraint> getFlexibleConstraints() { 398 return iFlexibleConstraints; 399 } 400 401 @Override 402 public double getTotalValue() { 403 double ret = 0; 404 for (Criterion<Lecture, Placement> criterion: getCriteria()) 405 ret += criterion.getWeightedValue(); 406 return ret; 407 } 408 409 @Override 410 public double getTotalValue(Collection<Lecture> variables) { 411 double ret = 0; 412 for (Criterion<Lecture, Placement> criterion: getCriteria()) 413 ret += criterion.getWeightedValue(variables); 414 return ret; 415 } 416 417 public int getYear() { 418 return iYear; 419 } 420 421 public void setYear(int year) { 422 iYear = year; 423 } 424 425 public Set<Student> getAllStudents() { 426 return iAllStudents; 427 } 428 429 public void addStudent(Student student) { 430 iAllStudents.add(student); 431 } 432 433 public void removeStudent(Student student) { 434 iAllStudents.remove(student); 435 } 436 437 /** 438 * Returns amount of allocated memory. 439 * 440 * @return amount of allocated memory to be written in the log 441 */ 442 public static synchronized String getMem() { 443 Runtime rt = Runtime.getRuntime(); 444 return sDoubleFormat.format(((double) (rt.totalMemory() - rt.freeMemory())) / 1048576) + "M"; 445 } 446 447 448 /** 449 * Returns the set of conflicting variables with this value, if it is 450 * assigned to its variable. Conflicts with constraints that implement 451 * {@link WeakeningConstraint} are ignored. 452 */ 453 public Set<Placement> conflictValuesSkipWeakeningConstraints(Placement value) { 454 Set<Placement> conflictValues = new HashSet<Placement>(); 455 for (Constraint<Lecture, Placement> constraint : value.variable().hardConstraints()) { 456 if (constraint instanceof WeakeningConstraint) continue; 457 constraint.computeConflicts(value, conflictValues); 458 } 459 for (GlobalConstraint<Lecture, Placement> constraint : globalConstraints()) { 460 if (constraint instanceof WeakeningConstraint) continue; 461 constraint.computeConflicts(value, conflictValues); 462 } 463 return conflictValues; 464 } 465 466 /** 467 * The method creates date patterns (bitsets) which represent the weeks of a 468 * semester. 469 * 470 * @return a list of BitSets which represents the weeks of a semester. 471 */ 472 public List<BitSet> getWeeks() { 473 if (iWeeks == null) { 474 String defaultDatePattern = getProperties().getProperty("DatePattern.CustomDatePattern", null); 475 if (defaultDatePattern == null){ 476 defaultDatePattern = getProperties().getProperty("DatePattern.Default"); 477 } 478 if (defaultDatePattern == null) return null; 479 480 // Create default date pattern 481 BitSet fullTerm = new BitSet(defaultDatePattern.length()); 482 for (int i = 0; i < defaultDatePattern.length(); i++) { 483 if (defaultDatePattern.charAt(i) == 49) { 484 fullTerm.set(i); 485 } 486 } 487 488 // Cut date pattern into weeks (every week contains 7 positive bits) 489 iWeeks = new ArrayList<BitSet>(); 490 int cnt = 0; 491 for (int i = 0; i < fullTerm.length(); i++) { 492 if (fullTerm.get(i)) { 493 int w = (cnt++) / 7; 494 if (iWeeks.size() == w) { 495 iWeeks.add(new BitSet(fullTerm.length())); 496 } 497 iWeeks.get(w).set(i); 498 } 499 } 500 } 501 return iWeeks; 502 } 503}