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