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}