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