001    package net.sf.cpsolver.coursett;
002    
003    import java.io.File;
004    import java.text.SimpleDateFormat;
005    import java.util.ArrayList;
006    import java.util.BitSet;
007    import java.util.Calendar;
008    import java.util.Date;
009    import java.util.HashSet;
010    import java.util.HashMap;
011    import java.util.Hashtable;
012    import java.util.Iterator;
013    import java.util.List;
014    import java.util.Locale;
015    import java.util.Map;
016    import java.util.Set;
017    
018    import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint;
019    import net.sf.cpsolver.coursett.constraint.DepartmentSpreadConstraint;
020    import net.sf.cpsolver.coursett.constraint.DiscouragedRoomConstraint;
021    import net.sf.cpsolver.coursett.constraint.FlexibleConstraint.FlexibleConstraintType;
022    import net.sf.cpsolver.coursett.constraint.GroupConstraint;
023    import net.sf.cpsolver.coursett.constraint.IgnoreStudentConflictsConstraint;
024    import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
025    import net.sf.cpsolver.coursett.constraint.JenrlConstraint;
026    import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedGroupsOfTime;
027    import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedRoomsConstraint;
028    import net.sf.cpsolver.coursett.constraint.RoomConstraint;
029    import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
030    import net.sf.cpsolver.coursett.model.Configuration;
031    import net.sf.cpsolver.coursett.model.Lecture;
032    import net.sf.cpsolver.coursett.model.Placement;
033    import net.sf.cpsolver.coursett.model.RoomLocation;
034    import net.sf.cpsolver.coursett.model.RoomSharingModel;
035    import net.sf.cpsolver.coursett.model.Student;
036    import net.sf.cpsolver.coursett.model.TimeLocation;
037    import net.sf.cpsolver.coursett.model.TimetableModel;
038    import net.sf.cpsolver.ifs.model.Constraint;
039    import net.sf.cpsolver.ifs.solution.Solution;
040    import net.sf.cpsolver.ifs.solver.Solver;
041    import net.sf.cpsolver.ifs.util.Progress;
042    import net.sf.cpsolver.ifs.util.ToolBox;
043    
044    import org.dom4j.Document;
045    import org.dom4j.Element;
046    import org.dom4j.io.SAXReader;
047    
048    /**
049     * This class loads the input model from XML file. <br>
050     * <br>
051     * Parameters:
052     * <table border='1'>
053     * <tr>
054     * <th>Parameter</th>
055     * <th>Type</th>
056     * <th>Comment</th>
057     * </tr>
058     * <tr>
059     * <td>General.Input</td>
060     * <td>{@link String}</td>
061     * <td>Input XML file</td>
062     * </tr>
063     * <tr>
064     * <td>General.DeptBalancing</td>
065     * <td>{@link Boolean}</td>
066     * <td>Use {@link DepartmentSpreadConstraint}</td>
067     * </tr>
068     * <tr>
069     * <td>General.InteractiveMode</td>
070     * <td>{@link Boolean}</td>
071     * <td>Interactive mode (see {@link Lecture#purgeInvalidValues(boolean)})</td>
072     * </tr>
073     * <tr>
074     * <td>General.ForcedPerturbances</td>
075     * <td>{@link Integer}</td>
076     * <td>For testing of MPP: number of input perturbations, i.e., classes with
077     * prohibited intial assignment</td>
078     * </tr>
079     * <tr>
080     * <td>General.UseDistanceConstraints</td>
081     * <td>{@link Boolean}</td>
082     * <td>Consider distances between buildings</td>
083     * </tr>
084     * </table>
085     * 
086     * @version CourseTT 1.2 (University Course Timetabling)<br>
087     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
088     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
089     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
090     * <br>
091     *          This library is free software; you can redistribute it and/or modify
092     *          it under the terms of the GNU Lesser General Public License as
093     *          published by the Free Software Foundation; either version 3 of the
094     *          License, or (at your option) any later version. <br>
095     * <br>
096     *          This library is distributed in the hope that it will be useful, but
097     *          WITHOUT ANY WARRANTY; without even the implied warranty of
098     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
099     *          Lesser General Public License for more details. <br>
100     * <br>
101     *          You should have received a copy of the GNU Lesser General Public
102     *          License along with this library; if not see
103     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
104     */
105    
106    public class TimetableXMLLoader extends TimetableLoader {
107        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableXMLLoader.class);
108        private static SimpleDateFormat sDF = new SimpleDateFormat("MM/dd");
109    
110        private boolean iDeptBalancing = true;
111        private int iForcedPerturbances = 0;
112    
113        private boolean iInteractiveMode = false;
114        private File iInputFile;
115    
116        private Progress iProgress = null;
117    
118        public TimetableXMLLoader(TimetableModel model) {
119            super(model);
120            iProgress = Progress.getInstance(getModel());
121            iInputFile = new File(getModel().getProperties().getProperty("General.Input",
122                    "." + File.separator + "solution.xml"));
123            iForcedPerturbances = getModel().getProperties().getPropertyInt("General.ForcedPerturbances", 0);
124            iDeptBalancing = getModel().getProperties().getPropertyBoolean("General.DeptBalancing", true);
125            iInteractiveMode = getModel().getProperties().getPropertyBoolean("General.InteractiveMode", iInteractiveMode);
126        }
127    
128        private Solver<Lecture, Placement> iSolver = null;
129    
130        public void setSolver(Solver<Lecture, Placement> solver) {
131            iSolver = solver;
132        }
133    
134        public Solver<Lecture, Placement> getSolver() {
135            return iSolver;
136        }
137    
138        public void setInputFile(File inputFile) {
139            iInputFile = inputFile;
140        }
141    
142        @Override
143        public void load() throws Exception {
144            load(null);
145        }
146    
147        public void load(Solution<Lecture, Placement> currentSolution) throws Exception {
148            sLogger.debug("Reading XML data from " + iInputFile);
149            iProgress.setPhase("Reading " + iInputFile.getName() + " ...");
150    
151            Document document = (new SAXReader()).read(iInputFile);
152            Element root = document.getRootElement();
153            sLogger.debug("Root element: " + root.getName());
154            if (!"llrt".equals(root.getName()) && !"timetable".equals(root.getName())) {
155                sLogger.error("Given XML file is not large lecture room timetabling problem.");
156                return;
157            }
158    
159            iProgress.load(root, true);
160            iProgress.message(Progress.MSGLEVEL_STAGE, "Restoring from backup ...");
161    
162            if (root.element("input") != null)
163                root = root.element("input");
164    
165            if (root.attributeValue("term") != null)
166                getModel().getProperties().setProperty("Data.Term", root.attributeValue("term"));
167            if (root.attributeValue("year") != null)
168                getModel().setYear(Integer.parseInt(root.attributeValue("year")));
169            else if (root.attributeValue("term") != null)
170                getModel().setYear(Integer.parseInt(root.attributeValue("term").substring(0, 4)));
171            if (root.attributeValue("initiative") != null)
172                getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative"));
173            if (root.attributeValue("semester") != null && root.attributeValue("year") != null)
174                getModel().getProperties().setProperty("Data.Term",
175                        root.attributeValue("semester") + root.attributeValue("year"));
176            if (root.attributeValue("session") != null)
177                getModel().getProperties().setProperty("General.SessionId", root.attributeValue("session"));
178            if (root.attributeValue("solverGroup") != null)
179                getModel().getProperties().setProperty("General.SolverGroupId", root.attributeValue("solverGroup"));
180            String version = root.attributeValue("version");
181           
182            // Student sectioning considers the whole course (including committed classes), since 2.5
183            boolean sectionWholeCourse = true;
184            
185            if (version != null && version.indexOf('.') >= 0) {
186                int majorVersion = Integer.parseInt(version.substring(0, version.indexOf('.')));
187                int minorVersion = Integer.parseInt(version.substring(1 + version.indexOf('.')));
188                
189                sectionWholeCourse = (majorVersion == 2 && minorVersion >= 5) || majorVersion > 2;
190            }
191            
192            HashMap<Long, TimeLocation> perts = new HashMap<Long, TimeLocation>();
193            if (getModel().getProperties().getPropertyInt("MPP.TimePert", 0) > 0) {
194                int nrChanges = getModel().getProperties().getPropertyInt("MPP.TimePert", 0);
195                int idx = 0;
196                for (Iterator<?> i = root.element("perturbations").elementIterator("class"); i.hasNext() && idx < nrChanges; idx++) {
197                    Element pertEl = (Element) i.next();
198                    Long classId = Long.valueOf(pertEl.attributeValue("id"));
199                    TimeLocation tl = new TimeLocation(Integer.parseInt(pertEl.attributeValue("days"), 2), Integer
200                            .parseInt(pertEl.attributeValue("start")), Integer.parseInt(pertEl.attributeValue("length")),
201                            0, 0.0, 0, null, null, null, 0);
202                    perts.put(classId, tl);
203                }
204            }
205    
206            iProgress.setPhase("Creating rooms ...", root.element("rooms").elements("room").size());
207            HashMap<String, Element> roomElements = new HashMap<String, Element>();
208            HashMap<String, RoomConstraint> roomConstraints = new HashMap<String, RoomConstraint>();
209            HashMap<Long, List<Lecture>> sameLectures = new HashMap<Long, List<Lecture>>();
210            for (Iterator<?> i = root.element("rooms").elementIterator("room"); i.hasNext();) {
211                Element roomEl = (Element) i.next();
212                iProgress.incProgress();
213                roomElements.put(roomEl.attributeValue("id"), roomEl);
214                if ("false".equals(roomEl.attributeValue("constraint")))
215                    continue;
216                RoomSharingModel sharingModel = null;
217                Element sharingEl = roomEl.element("sharing");
218                if (sharingEl != null) {
219                    Character freeForAllPrefChar = null;
220                    Element freeForAllEl = sharingEl.element("freeForAll");
221                    if (freeForAllEl != null)
222                        freeForAllPrefChar = freeForAllEl.attributeValue("value", "F").charAt(0);
223                    Character notAvailablePrefChar = null;
224                    Element notAvailableEl = sharingEl.element("notAvailable");
225                    if (notAvailableEl != null)
226                        notAvailablePrefChar = notAvailableEl.attributeValue("value", "X").charAt(0);
227                    String pattern = sharingEl.element("pattern").getText();
228                    int unit = Integer.parseInt(sharingEl.element("pattern").attributeValue("unit", "1"));
229                    java.util.List<?> depts = sharingEl.elements("department");
230                    Long departmentIds[] = new Long[depts.size()];
231                    for (int j = 0; j < departmentIds.length; j++)
232                        departmentIds[j] = Long.valueOf(((Element) depts.get(j)).attributeValue("id"));
233                    sharingModel = new RoomSharingModel(unit, departmentIds, pattern, freeForAllPrefChar, notAvailablePrefChar);
234                }
235                boolean ignoreTooFar = false;
236                if ("true".equals(roomEl.attributeValue("ignoreTooFar")))
237                    ignoreTooFar = true;
238                boolean fake = false;
239                if ("true".equals(roomEl.attributeValue("fake")))
240                    fake = true;
241                Double posX = null, posY = null;
242                if (roomEl.attributeValue("location") != null) {
243                    String loc = roomEl.attributeValue("location");
244                    posX = Double.valueOf(loc.substring(0, loc.indexOf(',')));
245                    posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1));
246                }
247                boolean discouraged = "true".equals(roomEl.attributeValue("discouraged"));
248                RoomConstraint constraint = (discouraged ? new DiscouragedRoomConstraint(
249                        getModel().getProperties(),
250                        Long.valueOf(roomEl.attributeValue("id")),
251                        (roomEl.attributeValue("name") != null ? roomEl.attributeValue("name") : "r"
252                                + roomEl.attributeValue("id")),
253                        (roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building"))),
254                        Integer.parseInt(roomEl.attributeValue("capacity")), sharingModel, posX, posY, ignoreTooFar, !fake)
255                        : new RoomConstraint(Long.valueOf(roomEl.attributeValue("id")),
256                                (roomEl.attributeValue("name") != null ? roomEl.attributeValue("name") : "r"
257                                        + roomEl.attributeValue("id")), (roomEl.attributeValue("building") == null ? null
258                                        : Long.valueOf(roomEl.attributeValue("building"))), Integer.parseInt(roomEl
259                                        .attributeValue("capacity")), sharingModel, posX, posY, ignoreTooFar, !fake));
260                if (roomEl.attributeValue("type") != null)
261                    constraint.setType(Long.valueOf(roomEl.attributeValue("type")));
262                getModel().addConstraint(constraint);
263                roomConstraints.put(roomEl.attributeValue("id"), constraint);
264                
265                for (Iterator<?> j = roomEl.elementIterator("travel-time"); j.hasNext();) {
266                    Element travelTimeEl = (Element)j.next();
267                    getModel().getDistanceMetric().addTravelTime(constraint.getResourceId(),
268                            Long.valueOf(travelTimeEl.attributeValue("id")),
269                            Integer.valueOf(travelTimeEl.attributeValue("minutes")));
270                }
271            }
272    
273            HashMap<String, InstructorConstraint> instructorConstraints = new HashMap<String, InstructorConstraint>();
274            if (root.element("instructors") != null) {
275                for (Iterator<?> i = root.element("instructors").elementIterator("instructor"); i.hasNext();) {
276                    Element instructorEl = (Element) i.next();
277                    InstructorConstraint instructorConstraint = new InstructorConstraint(Long.valueOf(instructorEl
278                            .attributeValue("id")), instructorEl.attributeValue("puid"), (instructorEl
279                            .attributeValue("name") != null ? instructorEl.attributeValue("name") : "i"
280                            + instructorEl.attributeValue("id")), "true".equals(instructorEl.attributeValue("ignDist")));
281                    if (instructorEl.attributeValue("type") != null)
282                        instructorConstraint.setType(Long.valueOf(instructorEl.attributeValue("type")));
283                    instructorConstraints.put(instructorEl.attributeValue("id"), instructorConstraint);
284    
285                    getModel().addConstraint(instructorConstraint);
286                }
287            }
288            HashMap<Long, String> depts = new HashMap<Long, String>();
289            if (root.element("departments") != null) {
290                for (Iterator<?> i = root.element("departments").elementIterator("department"); i.hasNext();) {
291                    Element deptEl = (Element) i.next();
292                    depts.put(Long.valueOf(deptEl.attributeValue("id")), (deptEl.attributeValue("name") != null ? deptEl
293                            .attributeValue("name") : "d" + deptEl.attributeValue("id")));
294                }
295            }
296    
297            HashMap<Long, Configuration> configs = new HashMap<Long, Configuration>();
298            HashMap<Long, List<Configuration>> alternativeConfigurations = new HashMap<Long, List<Configuration>>();
299            if (root.element("configurations") != null) {
300                for (Iterator<?> i = root.element("configurations").elementIterator("config"); i.hasNext();) {
301                    Element configEl = (Element) i.next();
302                    Long configId = Long.valueOf(configEl.attributeValue("id"));
303                    int limit = Integer.parseInt(configEl.attributeValue("limit"));
304                    Long offeringId = Long.valueOf(configEl.attributeValue("offering"));
305                    Configuration config = new Configuration(offeringId, configId, limit);
306                    configs.put(configId, config);
307                    List<Configuration> altConfigs = alternativeConfigurations.get(offeringId);
308                    if (altConfigs == null) {
309                        altConfigs = new ArrayList<Configuration>();
310                        alternativeConfigurations.put(offeringId, altConfigs);
311                    }
312                    altConfigs.add(config);
313                    config.setAltConfigurations(altConfigs);
314                }
315            }
316    
317            iProgress.setPhase("Creating variables ...", root.element("classes").elements("class").size());
318    
319            HashMap<String, Element> classElements = new HashMap<String, Element>();
320            HashMap<String, Lecture> lectures = new HashMap<String, Lecture>();
321            HashMap<Lecture, Placement> assignedPlacements = new HashMap<Lecture, Placement>();
322            HashMap<Lecture, String> parents = new HashMap<Lecture, String>();
323            int ord = 0;
324            for (Iterator<?> i1 = root.element("classes").elementIterator("class"); i1.hasNext();) {
325                Element classEl = (Element) i1.next();
326    
327                Configuration config = null;
328                if (classEl.attributeValue("config") != null) {
329                    config = configs.get(Long.valueOf(classEl.attributeValue("config")));
330                }
331                if (config == null && classEl.attributeValue("offering") != null) {
332                    Long offeringId = Long.valueOf(classEl.attributeValue("offering"));
333                    Long configId = Long.valueOf(classEl.attributeValue("config"));
334                    List<Configuration> altConfigs = alternativeConfigurations.get(offeringId);
335                    if (altConfigs == null) {
336                        altConfigs = new ArrayList<Configuration>();
337                        alternativeConfigurations.put(offeringId, altConfigs);
338                    }
339                    for (Configuration c : altConfigs) {
340                        if (c.getConfigId().equals(configId)) {
341                            config = c;
342                            break;
343                        }
344                    }
345                    if (config == null) {
346                        config = new Configuration(offeringId, configId, -1);
347                        altConfigs.add(config);
348                        config.setAltConfigurations(altConfigs);
349                    }
350                }
351    
352                DatePattern defaultDatePattern = new DatePattern();
353                if (classEl.attributeValue("dates") == null) {
354                    int startDay = Integer.parseInt(classEl.attributeValue("startDay", "0"));
355                    int endDay = Integer.parseInt(classEl.attributeValue("endDay", "1"));
356                    defaultDatePattern.setPattern(startDay, endDay);
357                    defaultDatePattern.setName(sDF.format(getDate(getModel().getYear(), startDay)) + "-" + sDF.format(getDate(getModel().getYear(), endDay)));
358                } else {
359                    defaultDatePattern.setId(classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern")));
360                    defaultDatePattern.setName(classEl.attributeValue("datePatternName"));
361                    defaultDatePattern.setPattern(classEl.attributeValue("dates"));
362                }
363                Hashtable<Long, DatePattern> datePatterns = new Hashtable<Long, TimetableXMLLoader.DatePattern>();
364                for (Iterator<?> i2 = classEl.elementIterator("date"); i2.hasNext();) {
365                    Element dateEl = (Element) i2.next();
366                    Long id = Long.valueOf(dateEl.attributeValue("id"));
367                    datePatterns.put(id, new DatePattern(
368                            id,
369                            dateEl.attributeValue("name"),
370                            dateEl.attributeValue("pattern")));
371                }
372                classElements.put(classEl.attributeValue("id"), classEl);
373                List<InstructorConstraint> ics = new ArrayList<InstructorConstraint>();
374                for (Iterator<?> i2 = classEl.elementIterator("instructor"); i2.hasNext();) {
375                    Element instructorEl = (Element) i2.next();
376                    InstructorConstraint instructorConstraint = instructorConstraints
377                            .get(instructorEl.attributeValue("id"));
378                    if (instructorConstraint == null) {
379                        instructorConstraint = new InstructorConstraint(Long.valueOf(instructorEl.attributeValue("id")),
380                                instructorEl.attributeValue("puid"),
381                                (instructorEl.attributeValue("name") != null ? instructorEl.attributeValue("name") : "i"
382                                        + instructorEl.attributeValue("id")), "true".equals(instructorEl
383                                        .attributeValue("ignDist")));
384                        instructorConstraints.put(instructorEl.attributeValue("id"), instructorConstraint);
385                        getModel().addConstraint(instructorConstraint);
386                    }
387                    ics.add(instructorConstraint);
388                }
389                List<RoomLocation> roomLocations = new ArrayList<RoomLocation>();
390                List<RoomConstraint> roomConstraintsThisClass = new ArrayList<RoomConstraint>();
391                List<RoomLocation> initialRoomLocations = new ArrayList<RoomLocation>();
392                List<RoomLocation> assignedRoomLocations = new ArrayList<RoomLocation>();
393                List<RoomLocation> bestRoomLocations = new ArrayList<RoomLocation>();
394                for (Iterator<?> i2 = classEl.elementIterator("room"); i2.hasNext();) {
395                    Element roomLocationEl = (Element) i2.next();
396                    Element roomEl = roomElements.get(roomLocationEl.attributeValue("id"));
397                    RoomConstraint roomConstraint = roomConstraints.get(roomLocationEl.attributeValue("id"));
398    
399                    Long roomId = null;
400                    String roomName = null;
401                    Long bldgId = null;
402    
403                    if (roomConstraint != null) {
404                        roomConstraintsThisClass.add(roomConstraint);
405                        roomId = roomConstraint.getResourceId();
406                        roomName = roomConstraint.getRoomName();
407                        bldgId = roomConstraint.getBuildingId();
408                    } else {
409                        roomId = Long.valueOf(roomEl.attributeValue("id"));
410                        roomName = (roomEl.attributeValue("name") != null ? roomEl.attributeValue("name") : "r"
411                                + roomEl.attributeValue("id"));
412                        bldgId = (roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl
413                                .attributeValue("building")));
414                    }
415    
416                    boolean ignoreTooFar = false;
417                    if ("true".equals(roomEl.attributeValue("ignoreTooFar")))
418                        ignoreTooFar = true;
419                    Double posX = null, posY = null;
420                    if (roomEl.attributeValue("location") != null) {
421                        String loc = roomEl.attributeValue("location");
422                        posX = Double.valueOf(loc.substring(0, loc.indexOf(',')));
423                        posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1));
424                    }
425                    RoomLocation rl = new RoomLocation(roomId, roomName, bldgId, Integer.parseInt(roomLocationEl
426                            .attributeValue("pref")), Integer.parseInt(roomEl.attributeValue("capacity")), posX, posY,
427                            ignoreTooFar, roomConstraint);
428                    if ("true".equals(roomLocationEl.attributeValue("initial")))
429                        initialRoomLocations.add(rl);
430                    if ("true".equals(roomLocationEl.attributeValue("solution")))
431                        assignedRoomLocations.add(rl);
432                    if ("true".equals(roomLocationEl.attributeValue("best")))
433                        bestRoomLocations.add(rl);
434                    roomLocations.add(rl);
435                }
436                List<TimeLocation> timeLocations = new ArrayList<TimeLocation>();
437                TimeLocation initialTimeLocation = null;
438                TimeLocation assignedTimeLocation = null;
439                TimeLocation bestTimeLocation = null;
440                TimeLocation prohibitedTime = perts.get(Long.valueOf(classEl.attributeValue("id")));
441                
442                for (Iterator<?> i2 = classEl.elementIterator("time"); i2.hasNext();) {
443                    Element timeLocationEl = (Element) i2.next();
444                    DatePattern dp = defaultDatePattern;
445                    if (timeLocationEl.attributeValue("date") != null)
446                        dp = datePatterns.get(Long.valueOf(timeLocationEl.attributeValue("date")));
447                    TimeLocation tl = new TimeLocation(
448                            Integer.parseInt(timeLocationEl.attributeValue("days"), 2),
449                            Integer.parseInt(timeLocationEl.attributeValue("start")),
450                            Integer.parseInt(timeLocationEl.attributeValue("length")),
451                            (int) Double.parseDouble(timeLocationEl.attributeValue("pref")),
452                            Double.parseDouble(timeLocationEl.attributeValue("npref", timeLocationEl.attributeValue("pref"))),
453                            Integer.parseInt(timeLocationEl.attributeValue("datePref", "0")),
454                            dp.getId(), dp.getName(), dp.getPattern(),
455                            Integer.parseInt(timeLocationEl.attributeValue("breakTime") == null ? "-1" : timeLocationEl.attributeValue("breakTime")));
456                    if (tl.getBreakTime() < 0) tl.setBreakTime(tl.getLength() == 18 ? 15 : 10);
457                    if (timeLocationEl.attributeValue("pattern") != null)
458                        tl.setTimePatternId(Long.valueOf(timeLocationEl.attributeValue("pattern")));
459                    /*
460                     * if (timePatternTransform) tl =
461                     * transformTimePattern(Long.valueOf
462                     * (classEl.attributeValue("id")),tl);
463                     */
464                    if (prohibitedTime != null && prohibitedTime.getDayCode() == tl.getDayCode()
465                            && prohibitedTime.getStartSlot() == tl.getStartSlot()
466                            && prohibitedTime.getLength() == tl.getLength()) {
467                        sLogger.info("Time " + tl.getLongName() + " is prohibited for class "
468                                + classEl.attributeValue("id"));
469                        continue;
470                    }
471                    if ("true".equals(timeLocationEl.attributeValue("solution")))
472                        assignedTimeLocation = tl;
473                    if ("true".equals(timeLocationEl.attributeValue("initial")))
474                        initialTimeLocation = tl;
475                    if ("true".equals(timeLocationEl.attributeValue("best")))
476                        bestTimeLocation = tl;
477                    timeLocations.add(tl);
478                }
479                if (timeLocations.isEmpty()) {
480                    sLogger.error("  ERROR: No time.");
481                    continue;
482                }
483    
484                int minClassLimit = 0;
485                int maxClassLimit = 0;
486                float room2limitRatio = 1.0f;
487                if (!"true".equals(classEl.attributeValue("committed"))) {
488                    if (classEl.attributeValue("expectedCapacity") != null) {
489                        minClassLimit = maxClassLimit = Integer.parseInt(classEl.attributeValue("expectedCapacity"));
490                        int roomCapacity = Integer.parseInt(classEl.attributeValue("roomCapacity", classEl
491                                .attributeValue("expectedCapacity")));
492                        if (minClassLimit == 0)
493                            minClassLimit = maxClassLimit = roomCapacity;
494                        room2limitRatio = (minClassLimit == 0 ? 1.0f : ((float) roomCapacity) / minClassLimit);
495                    } else {
496                        if (classEl.attribute("classLimit") != null) {
497                            minClassLimit = maxClassLimit = Integer.parseInt(classEl.attributeValue("classLimit"));
498                        } else {
499                            minClassLimit = Integer.parseInt(classEl.attributeValue("minClassLimit"));
500                            maxClassLimit = Integer.parseInt(classEl.attributeValue("maxClassLimit"));
501                        }
502                        room2limitRatio = Float.parseFloat(classEl.attributeValue("roomToLimitRatio", "1.0"));
503                    }
504                }
505    
506                Lecture lecture = new Lecture(Long.valueOf(classEl.attributeValue("id")),
507                        (classEl.attributeValue("solverGroup") != null ? Long
508                                .valueOf(classEl.attributeValue("solverGroup")) : null), Long.valueOf(classEl
509                                .attributeValue("subpart", classEl.attributeValue("course", "-1"))), (classEl
510                                .attributeValue("name") != null ? classEl.attributeValue("name") : "c"
511                                + classEl.attributeValue("id")), timeLocations, roomLocations, Integer.parseInt(classEl
512                                .attributeValue("nrRooms", roomLocations.isEmpty() ? "0" : "1")), null, minClassLimit, maxClassLimit, room2limitRatio);
513                lecture.setNote(classEl.attributeValue("note"));
514    
515                if ("true".equals(classEl.attributeValue("committed")))
516                    lecture.setCommitted(true);
517    
518                if (!lecture.isCommitted() && classEl.attributeValue("ord") != null)
519                    lecture.setOrd(Integer.parseInt(classEl.attributeValue("ord")));
520                else
521                    lecture.setOrd(ord++);
522    
523                if (config != null)
524                    lecture.setConfiguration(config);
525    
526                if (initialTimeLocation != null && initialRoomLocations.size() == lecture.getNrRooms()) {
527                    lecture.setInitialAssignment(new Placement(lecture, initialTimeLocation, initialRoomLocations));
528                }
529                if (assignedTimeLocation != null && assignedRoomLocations.size() == lecture.getNrRooms()) {
530                    assignedPlacements.put(lecture, new Placement(lecture, assignedTimeLocation, assignedRoomLocations));
531                } else if (lecture.getInitialAssignment() != null) {
532                    assignedPlacements.put(lecture, lecture.getInitialAssignment());
533                }
534                if (bestTimeLocation != null && bestRoomLocations.size() == lecture.getNrRooms()) {
535                    lecture.setBestAssignment(new Placement(lecture, bestTimeLocation, bestRoomLocations));
536                } else if (assignedTimeLocation != null && assignedRoomLocations.size() == lecture.getNrRooms()) {
537                    lecture.setBestAssignment(assignedPlacements.get(lecture));
538                }
539    
540                lectures.put(classEl.attributeValue("id"), lecture);
541                if (classEl.attributeValue("department") != null)
542                    lecture.setDepartment(Long.valueOf(classEl.attributeValue("department")));
543                if (classEl.attribute("scheduler") != null)
544                    lecture.setScheduler(Long.valueOf(classEl.attributeValue("scheduler")));
545                if ((sectionWholeCourse || !lecture.isCommitted()) && classEl.attributeValue("subpart", classEl.attributeValue("course")) != null) {
546                    Long subpartId = Long.valueOf(classEl.attributeValue("subpart", classEl.attributeValue("course")));
547                    List<Lecture> sames = sameLectures.get(subpartId);
548                    if (sames == null) {
549                        sames = new ArrayList<Lecture>();
550                        sameLectures.put(subpartId, sames);
551                    }
552                    sames.add(lecture);
553                }
554                String parent = classEl.attributeValue("parent");
555                if (parent != null)
556                    parents.put(lecture, parent);
557    
558                getModel().addVariable(lecture);
559    
560                if (lecture.isCommitted()) {
561                    Placement placement = assignedPlacements.get(lecture);
562                    if (classEl.attribute("assignment") != null)
563                        placement.setAssignmentId(Long.valueOf(classEl.attributeValue("assignment")));
564                    for (InstructorConstraint ic : ics)
565                        ic.setNotAvailable(placement);
566                    for (RoomConstraint rc : roomConstraintsThisClass)
567                        rc.setNotAvailable(placement);
568                } else {
569                    for (InstructorConstraint ic : ics)
570                        ic.addVariable(lecture);
571                    for (RoomConstraint rc : roomConstraintsThisClass)
572                        rc.addVariable(lecture);
573                }
574    
575                iProgress.incProgress();
576            }
577    
578            for (Map.Entry<Lecture, String> entry : parents.entrySet()) {
579                Lecture lecture = entry.getKey();
580                Lecture parent = lectures.get(entry.getValue());
581                if (parent == null) {
582                    iProgress.warn("Parent class " + entry.getValue() + " does not exists.");
583                } else {
584                    lecture.setParent(parent);
585                }
586            }
587    
588            iProgress.setPhase("Creating constraints ...", root.element("groupConstraints").elements("constraint").size());
589            HashMap<String, Element> grConstraintElements = new HashMap<String, Element>();
590            HashMap<String, Constraint<Lecture, Placement>> groupConstraints = new HashMap<String, Constraint<Lecture, Placement>>();
591            for (Iterator<?> i1 = root.element("groupConstraints").elementIterator("constraint"); i1.hasNext();) {
592                Element grConstraintEl = (Element) i1.next();
593                Constraint<Lecture, Placement> c = null;
594                if ("SPREAD".equals(grConstraintEl.attributeValue("type"))) {
595                    c = new SpreadConstraint(getModel().getProperties(), grConstraintEl.attributeValue("name", "spread"));
596                } else if ("MIN_ROOM_USE".equals(grConstraintEl.attributeValue("type"))) {
597                    c = new MinimizeNumberOfUsedRoomsConstraint(getModel().getProperties());
598                } else if ("CLASS_LIMIT".equals(grConstraintEl.attributeValue("type"))) {
599                    if (grConstraintEl.element("parentClass") == null) {
600                        c = new ClassLimitConstraint(Integer.parseInt(grConstraintEl.attributeValue("courseLimit")),
601                                grConstraintEl.attributeValue("name", "class-limit"));
602                    } else {
603                        String classId = grConstraintEl.element("parentClass").attributeValue("id");
604                        c = new ClassLimitConstraint(lectures.get(classId), grConstraintEl.attributeValue("name",
605                                "class-limit"));
606                    }
607                    if (grConstraintEl.attributeValue("delta") != null)
608                        ((ClassLimitConstraint) c).setClassLimitDelta(Integer.parseInt(grConstraintEl
609                                .attributeValue("delta")));
610                } else if ("MIN_GRUSE(10x1h)".equals(grConstraintEl.attributeValue("type"))) {
611                    c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "10x1h",
612                            MinimizeNumberOfUsedGroupsOfTime.sGroups10of1h);
613                } else if ("MIN_GRUSE(5x2h)".equals(grConstraintEl.attributeValue("type"))) {
614                    c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "5x2h",
615                            MinimizeNumberOfUsedGroupsOfTime.sGroups5of2h);
616                } else if ("MIN_GRUSE(3x3h)".equals(grConstraintEl.attributeValue("type"))) {
617                    c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "3x3h",
618                            MinimizeNumberOfUsedGroupsOfTime.sGroups3of3h);
619                } else if ("MIN_GRUSE(2x5h)".equals(grConstraintEl.attributeValue("type"))) {
620                    c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "2x5h",
621                            MinimizeNumberOfUsedGroupsOfTime.sGroups2of5h);
622                } else if (IgnoreStudentConflictsConstraint.REFERENCE.equals(grConstraintEl.attributeValue("type"))) {
623                    c = new IgnoreStudentConflictsConstraint();
624                } else {
625                    try {
626                        FlexibleConstraintType f = FlexibleConstraintType.valueOf(grConstraintEl.attributeValue("type"));
627                        try {
628                            c = f.create(
629                                    Long.valueOf(grConstraintEl.attributeValue("id")),
630                                    grConstraintEl.attributeValue("owner"),
631                                    grConstraintEl.attributeValue("pref"),
632                                    grConstraintEl.attributeValue("reference"));
633                        } catch (IllegalArgumentException e) {
634                                iProgress.warn("Failed to create flexible constraint " + grConstraintEl.attributeValue("type") + ": " + e.getMessage(), e);
635                                continue;
636                        }
637                    } catch (IllegalArgumentException e) {
638                        // type did not match, continue with group constraint types
639                        c = new GroupConstraint(
640                                Long.valueOf(grConstraintEl.attributeValue("id")),
641                                GroupConstraint.ConstraintType.get(grConstraintEl.attributeValue("type")),
642                                grConstraintEl.attributeValue("pref"));
643                    }
644                }
645                getModel().addConstraint(c);
646                for (Iterator<?> i2 = grConstraintEl.elementIterator("class"); i2.hasNext();) {
647                    String classId = ((Element) i2.next()).attributeValue("id");
648                    Lecture other = lectures.get(classId);
649                    if (other != null)
650                        c.addVariable(other);
651                    else
652                        iProgress.warn("Class " + classId + " does not exists, but it is referred from group constraint " + c.getId() + " (" + c.getName() + ")");
653                }
654                grConstraintElements.put(grConstraintEl.attributeValue("id"), grConstraintEl);
655                groupConstraints.put(grConstraintEl.attributeValue("id"), c);
656                iProgress.incProgress();
657            }   
658    
659            iProgress.setPhase("Loading students ...", root.element("students").elements("student").size());
660            boolean initialSectioning = true;
661            HashMap<Long, Student> students = new HashMap<Long, Student>();
662            HashMap<Long, Set<Student>> offering2students = new HashMap<Long, Set<Student>>();
663            for (Iterator<?> i1 = root.element("students").elementIterator("student"); i1.hasNext();) {
664                Element studentEl = (Element) i1.next();
665                List<Lecture> lecturesThisStudent = new ArrayList<Lecture>();
666                Long studentId = Long.valueOf(studentEl.attributeValue("id"));
667                Student student = students.get(studentId);
668                if (student == null) {
669                    student = new Student(studentId);
670                    students.put(studentId, student);
671                    getModel().addStudent(student);
672                }
673                student.setAcademicArea(studentEl.attributeValue("area"));
674                student.setAcademicClassification(studentEl.attributeValue("classification"));
675                student.setMajor(studentEl.attributeValue("major"));
676                student.setCurriculum(studentEl.attributeValue("curriculum"));
677                for (Iterator<?> i2 = studentEl.elementIterator("offering"); i2.hasNext();) {
678                    Element ofEl = (Element) i2.next();
679                    Long offeringId = Long.valueOf(ofEl.attributeValue("id"));
680                    String priority = ofEl.attributeValue("priority");
681                    student.addOffering(offeringId, Double.parseDouble(ofEl.attributeValue("weight", "1.0")), priority == null ? null : Double.valueOf(priority));
682                    Set<Student> studentsThisOffering = offering2students.get(offeringId);
683                    if (studentsThisOffering == null) {
684                        studentsThisOffering = new HashSet<Student>();
685                        offering2students.put(offeringId, studentsThisOffering);
686                    }
687                    studentsThisOffering.add(student);
688                }
689                for (Iterator<?> i2 = studentEl.elementIterator("class"); i2.hasNext();) {
690                    String classId = ((Element) i2.next()).attributeValue("id");
691                    Lecture lecture = lectures.get(classId);
692                    if (lecture == null) {
693                        iProgress.warn("Class " + classId + " does not exists, but it is referred from student " + student.getId());
694                        continue;
695                    }
696                    if (lecture.isCommitted()) {
697                        if (sectionWholeCourse && (lecture.getParent() != null || lecture.getConfiguration() != null)) {
698                            // committed, but with course structure -- sectioning can be used
699                            student.addLecture(lecture);
700                            lecture.addStudent(student);
701                            lecturesThisStudent.add(lecture);
702                            initialSectioning = false;
703                        } else {
704                            Placement placement = assignedPlacements.get(lecture);
705                            student.addCommitedPlacement(placement);
706                        }
707                    } else {
708                        student.addLecture(lecture);
709                        lecture.addStudent(student);
710                        lecturesThisStudent.add(lecture);
711                        initialSectioning = false;
712                    }
713                }
714    
715                for (Iterator<?> i2 = studentEl.elementIterator("prohibited-class"); i2.hasNext();) {
716                    String classId = ((Element) i2.next()).attributeValue("id");
717                    Lecture lecture = lectures.get(classId);
718                    if (lecture != null)
719                        student.addCanNotEnroll(lecture);
720                    else
721                        iProgress.warn("Class " + classId + " does not exists, but it is referred from student " + student.getId());
722                }
723                
724                if (studentEl.attributeValue("instructor") != null)
725                    student.setInstructor(instructorConstraints.get(studentEl.attributeValue("instructor")));
726    
727                iProgress.incProgress();
728            }
729    
730            for (List<Lecture> sames: sameLectures.values()) {
731                for (Lecture lect : sames) {
732                    lect.setSameSubpartLectures(sames);
733                }
734            }
735    
736            if (initialSectioning) {
737                iProgress.setPhase("Initial sectioning ...", offering2students.size());
738                for (Map.Entry<Long, Set<Student>> entry : offering2students.entrySet()) {
739                    Long offeringId = entry.getKey();
740                    Set<Student> studentsThisOffering = entry.getValue();
741                    List<Configuration> altConfigs = alternativeConfigurations.get(offeringId);
742                    getModel().getStudentSectioning().initialSectioning(offeringId, String.valueOf(offeringId), studentsThisOffering, altConfigs);
743                    iProgress.incProgress();
744                }
745                for (Student student: students.values()) {
746                    student.clearDistanceCache();
747                    if (student.getInstructor() != null)
748                        for (Lecture lecture: student.getInstructor().variables()) {
749                            student.addLecture(lecture);
750                            lecture.addStudent(student);
751                        }
752                }
753            }
754    
755            iProgress.setPhase("Computing jenrl ...", students.size());
756            HashMap<Lecture, HashMap<Lecture, JenrlConstraint>> jenrls = new HashMap<Lecture, HashMap<Lecture, JenrlConstraint>>();
757            for (Iterator<Student> i1 = students.values().iterator(); i1.hasNext();) {
758                Student st = i1.next();
759                for (Iterator<Lecture> i2 = st.getLectures().iterator(); i2.hasNext();) {
760                    Lecture l1 = i2.next();
761                    for (Iterator<Lecture> i3 = st.getLectures().iterator(); i3.hasNext();) {
762                        Lecture l2 = i3.next();
763                        if (l1.getId() >= l2.getId())
764                            continue;
765                        HashMap<Lecture, JenrlConstraint> x = jenrls.get(l1);
766                        if (x == null) {
767                            x = new HashMap<Lecture, JenrlConstraint>();
768                            jenrls.put(l1, x);
769                        }
770                        JenrlConstraint jenrl = x.get(l2);
771                        if (jenrl == null) {
772                            jenrl = new JenrlConstraint();
773                            jenrl.addVariable(l1);
774                            jenrl.addVariable(l2);
775                            getModel().addConstraint(jenrl);
776                            x.put(l2, jenrl);
777                        }
778                        jenrl.incJenrl(st);
779                    }
780                }
781                iProgress.incProgress();
782            }
783    
784            if (iDeptBalancing) {
785                iProgress.setPhase("Creating dept. spread constraints ...", getModel().variables().size());
786                HashMap<Long, DepartmentSpreadConstraint> depSpreadConstraints = new HashMap<Long, DepartmentSpreadConstraint>();
787                for (Lecture lecture : getModel().variables()) {
788                    if (lecture.getDepartment() == null)
789                        continue;
790                    DepartmentSpreadConstraint deptConstr = depSpreadConstraints.get(lecture.getDepartment());
791                    if (deptConstr == null) {
792                        String name = depts.get(lecture.getDepartment());
793                        deptConstr = new DepartmentSpreadConstraint(getModel().getProperties(), lecture.getDepartment(),
794                                (name != null ? name : "d" + lecture.getDepartment()));
795                        depSpreadConstraints.put(lecture.getDepartment(), deptConstr);
796                        getModel().addConstraint(deptConstr);
797                    }
798                    deptConstr.addVariable(lecture);
799                    iProgress.incProgress();
800                }
801            }
802    
803            if (getModel().getProperties().getPropertyBoolean("General.PurgeInvalidPlacements", true)) {
804                iProgress.setPhase("Purging invalid placements ...", getModel().variables().size());
805                for (Lecture lecture : getModel().variables()) {
806                    lecture.purgeInvalidValues(iInteractiveMode);
807                    iProgress.incProgress();
808                }            
809            }
810            
811            if (getModel().hasConstantVariables() && getModel().constantVariables().size() > 0) {
812                iProgress.setPhase("Assigning committed classes ...", assignedPlacements.size());
813                for (Map.Entry<Lecture, Placement> entry : assignedPlacements.entrySet()) {
814                    Lecture lecture = entry.getKey();
815                    Placement placement = entry.getValue();
816                    if (!lecture.isCommitted()) { iProgress.incProgress(); continue; }
817                    getModel().weaken(placement);
818                    Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel().conflictConstraints(placement);
819                    if (conflictConstraints.isEmpty()) {
820                        lecture.assign(0, placement);
821                    } else {
822                        sLogger.warn("WARNING: Unable to assign " + lecture.getName() + " := " + placement.getName());
823                        sLogger.debug("  Reason:");
824                        for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) {
825                            Set<Placement> vals = conflictConstraints.get(c);
826                            for (Placement v : vals) {
827                                sLogger.debug("    " + v.variable().getName() + " = " + v.getName());
828                            }
829                            sLogger.debug("    in constraint " + c);
830                        }
831                    }
832                    iProgress.incProgress();
833                }
834            }
835    
836            if (currentSolution != null) {
837                iProgress.setPhase("Creating best assignment ...", 2 * getModel().variables().size());
838                for (Lecture lecture : getModel().variables()) {
839                    iProgress.incProgress();
840                    Placement placement = lecture.getBestAssignment();
841                    if (placement == null) continue;
842                    getModel().weaken(placement);
843                    lecture.assign(0, placement);
844                }
845    
846                currentSolution.saveBest();
847                for (Lecture lecture : getModel().variables()) {
848                    iProgress.incProgress();
849                    if (lecture.getAssignment() != null)
850                        lecture.unassign(0);
851                }
852            }
853    
854            iProgress.setPhase("Creating initial assignment ...", assignedPlacements.size());
855            for (Map.Entry<Lecture, Placement> entry : assignedPlacements.entrySet()) {
856                Lecture lecture = entry.getKey();
857                Placement placement = entry.getValue();
858                if (lecture.isCommitted()) { iProgress.incProgress(); continue; }
859                getModel().weaken(placement);
860                Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel().conflictConstraints(placement);
861                if (conflictConstraints.isEmpty()) {
862                    if (!placement.isValid()) {
863                        sLogger.warn("WARNING: Lecture " + lecture.getName() + " does not contain assignment "
864                                + placement.getLongName() + " in its domain (" + placement.getNotValidReason() + ").");
865                    } else
866                        lecture.assign(0, placement);
867                } else {
868                    sLogger.warn("WARNING: Unable to assign " + lecture.getName() + " := " + placement.getName());
869                    sLogger.debug("  Reason:");
870                    for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) {
871                        Set<Placement> vals = conflictConstraints.get(c);
872                        for (Placement v : vals) {
873                            sLogger.debug("    " + v.variable().getName() + " = " + v.getName());
874                        }
875                        sLogger.debug("    in constraint " + c);
876                    }
877                }
878                iProgress.incProgress();
879            }
880    
881            if (initialSectioning && !getModel().assignedVariables().isEmpty()
882                    && !getModel().getProperties().getPropertyBoolean("Global.LoadStudentEnrlsFromSolution", false))
883                getModel().switchStudents();
884    
885            if (iForcedPerturbances > 0) {
886                iProgress.setPhase("Forcing perturbances", iForcedPerturbances);
887                for (int i = 0; i < iForcedPerturbances; i++) {
888                    iProgress.setProgress(i);
889                    Lecture var = null;
890                    do {
891                        var = ToolBox.random(getModel().variables());
892                    } while (var.getInitialAssignment() == null || var.values().size() <= 1);
893                    var.removeInitialValue();
894                }
895            }
896    
897            for (Constraint<Lecture, Placement> c : getModel().constraints()) {
898                if (c instanceof SpreadConstraint)
899                    ((SpreadConstraint) c).init();
900                if (c instanceof DiscouragedRoomConstraint)
901                    ((DiscouragedRoomConstraint) c).setEnabled(true);
902                if (c instanceof MinimizeNumberOfUsedRoomsConstraint)
903                    ((MinimizeNumberOfUsedRoomsConstraint) c).setEnabled(true);
904                if (c instanceof MinimizeNumberOfUsedGroupsOfTime)
905                    ((MinimizeNumberOfUsedGroupsOfTime) c).setEnabled(true);
906            }
907    
908            try {
909                getSolver().getClass().getMethod("load", new Class[] { Element.class }).invoke(getSolver(),
910                        new Object[] { root });
911            } catch (Exception e) {
912            }
913            
914            iProgress.setPhase("Done", 1);
915            iProgress.incProgress();
916    
917            sLogger.debug("Model successfully loaded.");
918            iProgress.info("Model successfully loaded.");
919        }
920    
921        public static Date getDate(int year, int dayOfYear) {
922            Calendar c = Calendar.getInstance(Locale.US);
923            c.set(year, 1, 1, 0, 0, 0);
924            c.set(Calendar.DAY_OF_YEAR, dayOfYear);
925            return c.getTime();
926        }
927        
928        public static class DatePattern {
929            Long iId;
930            String iName;
931            BitSet iPattern;
932            public DatePattern() {}
933            public DatePattern(Long id, String name, BitSet pattern) {
934                setId(id); setName(name); setPattern(pattern);
935            }
936            public DatePattern(Long id, String name, String pattern) {
937                setId(id); setName(name); setPattern(pattern);
938            }
939            public Long getId() { return iId; }
940            public void setId(Long id) { iId = id; }
941            public String getName() { return iName; }
942            public void setName(String name) { iName = name; }
943            public BitSet getPattern() { return iPattern; }
944            public void setPattern(BitSet pattern) { iPattern = pattern; }
945            public void setPattern(String pattern) {
946                iPattern = new BitSet(pattern.length());
947                for (int i = 0; i < pattern.length(); i++)
948                    if (pattern.charAt(i) == '1')
949                        iPattern.set(i);
950            }
951            public void setPattern(int startDay, int endDay) {
952                iPattern = new BitSet(366);
953                for (int d = startDay; d <= endDay; d++)
954                    iPattern.set(d);
955            }
956        }
957    }