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