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