001    package net.sf.cpsolver.studentsct;
002    
003    import java.io.File;
004    import java.util.BitSet;
005    import java.util.Enumeration;
006    import java.util.HashSet;
007    import java.util.Hashtable;
008    import java.util.Iterator;
009    import java.util.Vector;
010    
011    import org.dom4j.Document;
012    import org.dom4j.Element;
013    import org.dom4j.io.SAXReader;
014    
015    import net.sf.cpsolver.coursett.model.Placement;
016    import net.sf.cpsolver.coursett.model.RoomLocation;
017    import net.sf.cpsolver.coursett.model.TimeLocation;
018    import net.sf.cpsolver.ifs.util.Progress;
019    import net.sf.cpsolver.studentsct.filter.StudentFilter;
020    import net.sf.cpsolver.studentsct.model.AcademicAreaCode;
021    import net.sf.cpsolver.studentsct.model.Choice;
022    import net.sf.cpsolver.studentsct.model.Config;
023    import net.sf.cpsolver.studentsct.model.Course;
024    import net.sf.cpsolver.studentsct.model.CourseRequest;
025    import net.sf.cpsolver.studentsct.model.Enrollment;
026    import net.sf.cpsolver.studentsct.model.FreeTimeRequest;
027    import net.sf.cpsolver.studentsct.model.Offering;
028    import net.sf.cpsolver.studentsct.model.Request;
029    import net.sf.cpsolver.studentsct.model.Section;
030    import net.sf.cpsolver.studentsct.model.Student;
031    import net.sf.cpsolver.studentsct.model.Subpart;
032    
033    /**
034     * Load student sectioning model from an XML file.
035     *
036     * <br><br>
037     * Parameters:
038     * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
039     * <tr><td>General.Input</td><td>{@link String}</td><td>Path of an XML file to be loaded</td></tr>
040     * <tr><td>Xml.LoadBest</td><td>{@link Boolean}</td><td>If true, load best assignments</td></tr>
041     * <tr><td>Xml.LoadInitial</td><td>{@link Boolean}</td><td>If false, load initial assignments</td></tr>
042     * <tr><td>Xml.LoadCurrent</td><td>{@link Boolean}</td><td>If true, load current assignments</td></tr>
043     * <tr><td>Xml.LoadOfferings</td><td>{@link Boolean}</td><td>If true, load offerings (and their stucture, i.e., courses, configurations, subparts and sections)</td></tr>
044     * <tr><td>Xml.LoadStudents</td><td>{@link Boolean}</td><td>If true, load students (and their requests)</td></tr>
045     * <tr><td>Xml.StudentFilter</td><td>{@link StudentFilter}</td><td>If provided, students are filtered by the given student filter</td></tr>
046     * </table>
047     * 
048     * <br><br>
049     * Usage:<br>
050     * <code>
051     * StudentSectioningModel model = new StudentSectioningModel(cfg);<br>
052     * new StudentSectioningXMLLoader(model).load();<br>
053     * </code>
054     * 
055     * @version
056     * StudentSct 1.1 (Student Sectioning)<br>
057     * Copyright (C) 2007 Tomáš Müller<br>
058     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
059     * Lazenska 391, 76314 Zlin, Czech Republic<br>
060     * <br>
061     * This library is free software; you can redistribute it and/or
062     * modify it under the terms of the GNU Lesser General Public
063     * License as published by the Free Software Foundation; either
064     * version 2.1 of the License, or (at your option) any later version.
065     * <br><br>
066     * This library is distributed in the hope that it will be useful,
067     * but WITHOUT ANY WARRANTY; without even the implied warranty of
068     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
069     * Lesser General Public License for more details.
070     * <br><br>
071     * You should have received a copy of the GNU Lesser General Public
072     * License along with this library; if not, write to the Free Software
073     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
074     */
075    
076    public class StudentSectioningXMLLoader extends StudentSectioningLoader {
077        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(StudentSectioningXMLLoader.class);
078    
079        private File iInputFile;
080        private boolean iLoadBest = false;
081        private boolean iLoadInitial = false;
082        private boolean iLoadCurrent = false;
083        private boolean iLoadOfferings = true;
084        private boolean iLoadStudents = true;
085        private StudentFilter iStudentFilter = null;
086        
087        /**
088         * Constructor
089         * @param model student sectioning model
090         */
091        public StudentSectioningXMLLoader(StudentSectioningModel model) {
092            super(model);
093            iInputFile = new File(getModel().getProperties().getProperty("General.Input","."+File.separator+"solution.xml"));
094            iLoadBest = getModel().getProperties().getPropertyBoolean("Xml.LoadBest", true);
095            iLoadInitial = getModel().getProperties().getPropertyBoolean("Xml.LoadInitial", true);
096            iLoadCurrent = getModel().getProperties().getPropertyBoolean("Xml.LoadCurrent", true);
097            iLoadOfferings = getModel().getProperties().getPropertyBoolean("Xml.LoadOfferings", true);
098            iLoadStudents = getModel().getProperties().getPropertyBoolean("Xml.LoadStudents", true);
099            if (getModel().getProperties().getProperty("Xml.StudentFilter")!=null) {
100                try {
101                    iStudentFilter = (StudentFilter)
102                        Class.forName(getModel().getProperties().getProperty("Xml.StudentFilter")).
103                        getConstructor(new Class[]{}).
104                        newInstance(new Object[]{});
105                } catch (Exception e) {
106                    sLogger.error("Unable to create student filter, reason: "+e.getMessage(),e);
107                }
108            }
109        }
110    
111        /** Set input file (e.g., if it is not set by General.Input property) */
112        public void setInputFile(File inputFile) {
113            iInputFile=inputFile;
114        }
115        
116        /** Set student filter */
117        public void setStudentFilter(StudentFilter filter) {
118            iStudentFilter = filter;
119        }
120    
121        /** Set whether to load students */
122        public void setLoadStudents(boolean loadStudents) {
123            iLoadStudents = loadStudents;
124        }
125         
126        /** Set whether to load offerings */
127        public void setLoadOfferings(boolean loadOfferings) {
128            iLoadOfferings = loadOfferings;
129        }
130    
131        /** Create BitSet from a bit string */
132        private static BitSet createBitSet(String bitString) {
133            BitSet ret = new BitSet(bitString.length());
134            for (int i=0;i<bitString.length();i++)
135                if (bitString.charAt(i)=='1') ret.set(i);
136            return ret;
137        }
138    
139        /** Load the file */
140        public void load() throws Exception {
141            sLogger.debug("Reading XML data from "+iInputFile);
142            
143            Document document = (new SAXReader()).read(iInputFile);
144            Element root = document.getRootElement();
145            sLogger.debug("Root element: "+root.getName());
146            if (!"sectioning".equals(root.getName())) {
147                sLogger.error("Given XML file is not student sectioning problem.");
148                return;
149            }
150            
151            Progress.getInstance(getModel()).load(root, true);
152            Progress.getInstance(getModel()).message(Progress.MSGLEVEL_STAGE, "Restoring from backup ...");
153            
154            if (root.attributeValue("term")!=null)
155                getModel().getProperties().setProperty("Data.Term", root.attributeValue("term"));
156            if (root.attributeValue("year")!=null)
157                getModel().getProperties().setProperty("Data.Year", root.attributeValue("year"));
158            if (root.attributeValue("initiative")!=null)
159                getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative"));
160            String version = root.attributeValue("version");
161            
162            
163            Hashtable offeringTable = new Hashtable();
164            Hashtable courseTable = new Hashtable();
165            
166            if (iLoadOfferings && root.element("offerings")!=null) {
167                Hashtable subpartTable = new Hashtable();
168                Hashtable sectionTable = new Hashtable();
169                for (Iterator i=root.element("offerings").elementIterator("offering");i.hasNext();) {
170                    Element offeringEl = (Element)i.next();
171                    Offering offering = new Offering(
172                            Long.parseLong(offeringEl.attributeValue("id")),
173                            offeringEl.attributeValue("name","O"+offeringEl.attributeValue("id")));
174                    offeringTable.put(new Long(offering.getId()), offering);
175                    getModel().addOffering(offering);
176                    for (Iterator j=offeringEl.elementIterator("course");j.hasNext();) {
177                        Element courseEl = (Element)j.next();
178                        Course course = new Course(
179                                Long.parseLong(courseEl.attributeValue("id")),
180                                courseEl.attributeValue("subjectArea",""),
181                                courseEl.attributeValue("courseNbr","C"+courseEl.attributeValue("id")),
182                                offering,
183                                Integer.parseInt(courseEl.attributeValue("limit","0")),
184                                Integer.parseInt(courseEl.attributeValue("projected","0"))
185                                );
186                        courseTable.put(new Long(course.getId()), course);
187                    }
188                    for (Iterator j=offeringEl.elementIterator("config");j.hasNext();) {
189                        Element configEl = (Element)j.next();
190                        Config config = new Config(
191                                Long.parseLong(configEl.attributeValue("id")),
192                                configEl.attributeValue("name","G"+configEl.attributeValue("id")),
193                                offering);
194                        for (Iterator k=configEl.elementIterator("subpart");k.hasNext();) {
195                            Element subpartEl = (Element)k.next();
196                            Subpart parentSubpart = null;
197                            if (subpartEl.attributeValue("parent")!=null)
198                                parentSubpart = (Subpart)subpartTable.get(Long.valueOf(subpartEl.attributeValue("parent")));
199                            Subpart subpart = new Subpart(
200                                    Long.parseLong(subpartEl.attributeValue("id")),
201                                    subpartEl.attributeValue("itype"),
202                                    subpartEl.attributeValue("name","P"+subpartEl.attributeValue("id")),
203                                    config,
204                                    parentSubpart);
205                            subpartTable.put(new Long(subpart.getId()),subpart);
206                            for (Iterator l=subpartEl.elementIterator("section");l.hasNext();) {
207                                Element sectionEl = (Element)l.next();
208                                Section parentSection = null;
209                                if (sectionEl.attributeValue("parent")!=null)
210                                    parentSection = (Section)sectionTable.get(Long.valueOf(sectionEl.attributeValue("parent")));
211                                TimeLocation time = null;
212                                Element timeEl = sectionEl.element("time");
213                                if (timeEl!=null) {
214                                    time = new TimeLocation(
215                                        Integer.parseInt(timeEl.attributeValue("days"),2),
216                                        Integer.parseInt(timeEl.attributeValue("start")),
217                                        Integer.parseInt(timeEl.attributeValue("length")),
218                                        0, 0, 
219                                        timeEl.attributeValue("datePattern")==null?null:Long.valueOf(timeEl.attributeValue("datePattern")),
220                                        timeEl.attributeValue("datePatternName",""),
221                                        createBitSet(timeEl.attributeValue("dates")),
222                                        Integer.parseInt(timeEl.attributeValue("breakTime","0")));
223                                   if (timeEl.attributeValue("pattern")!=null)
224                                        time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
225                                }
226                                Vector rooms = new Vector();
227                                for (Iterator m=sectionEl.elementIterator("room");m.hasNext();) {
228                                    Element roomEl = (Element)m.next();
229                                    int posX=-1, posY=-1;
230                                    if (roomEl.attributeValue("location")!=null) {
231                                        String loc = roomEl.attributeValue("location");
232                                        posX = Integer.parseInt(loc.substring(0,loc.indexOf(',')));
233                                        posY = Integer.parseInt(loc.substring(loc.indexOf(',')+1));
234                                    }
235                                    RoomLocation room = new RoomLocation(
236                                        Long.valueOf(roomEl.attributeValue("id")),
237                                        roomEl.attributeValue("name","R"+roomEl.attributeValue("id")),
238                                        roomEl.attributeValue("building")==null?null:Long.valueOf(roomEl.attributeValue("building")),
239                                        0,
240                                        Integer.parseInt(roomEl.attributeValue("capacity")),
241                                        posX, posY, 
242                                        "true".equals(roomEl.attributeValue("ignoreTooFar")),
243                                        null);
244                                    rooms.add(room);                                
245                                }
246                                Placement placement = (time==null?null:new Placement(null, time, rooms));
247                                Section section = new Section(
248                                    Long.parseLong(sectionEl.attributeValue("id")),
249                                    Integer.parseInt(sectionEl.attributeValue("limit")),
250                                    sectionEl.attributeValue("name","S"+sectionEl.attributeValue("id")),
251                                    subpart,
252                                    placement,
253                                    sectionEl.attributeValue("instructorIds"),
254                                    sectionEl.attributeValue("instructorNames"),
255                                    parentSection);
256                                sectionTable.put(new Long(section.getId()), section);
257                                section.setSpaceHeld(Double.parseDouble(sectionEl.attributeValue("hold", "0.0")));
258                                section.setSpaceExpected(Double.parseDouble(sectionEl.attributeValue("expect", "0.0")));
259                            }
260                        }
261                    }
262                }
263            } else {
264                for (Enumeration e=getModel().getOfferings().elements();e.hasMoreElements();) {
265                    Offering offering = (Offering)e.nextElement();
266                    offeringTable.put(new Long(offering.getId()), offering);
267                    for (Enumeration f=offering.getCourses().elements();f.hasMoreElements();) {
268                        Course course = (Course)f.nextElement();
269                        courseTable.put(new Long(course.getId()), course);
270                    }
271                }
272            }
273            
274            if (iLoadStudents && root.element("students")!=null) {
275                Vector bestEnrollments = new Vector();
276                Vector currentEnrollments = new Vector();
277                for (Iterator i=root.element("students").elementIterator("student");i.hasNext();) {
278                    Element studentEl = (Element)i.next();
279                    Student student = new Student(
280                        Long.parseLong(studentEl.attributeValue("id")),
281                        "true".equals(studentEl.attributeValue("dummy")));
282                    for (Iterator j=studentEl.elementIterator();j.hasNext();) {
283                        Element requestEl = (Element)j.next();
284                        if ("classification".equals(requestEl.getName())) {
285                            student.getAcademicAreaClasiffications().add(new AcademicAreaCode(requestEl.attributeValue("area"),requestEl.attributeValue("code")));
286                        } else if ("major".equals(requestEl.getName())) {
287                            student.getMajors().add(new AcademicAreaCode(requestEl.attributeValue("area"),requestEl.attributeValue("code")));
288                        } else if ("minor".equals(requestEl.getName())) {
289                            student.getMinors().add(new AcademicAreaCode(requestEl.attributeValue("area"),requestEl.attributeValue("code")));
290                        }
291                    }
292                    if (iStudentFilter!=null && !iStudentFilter.accept(student))  continue;
293                    for (Iterator j=studentEl.elementIterator();j.hasNext();) {
294                        Element requestEl = (Element)j.next();
295                        if ("freeTime".equals(requestEl.getName())) {
296                            TimeLocation time = new TimeLocation(
297                                        Integer.parseInt(requestEl.attributeValue("days"),2),
298                                        Integer.parseInt(requestEl.attributeValue("start")),
299                                        Integer.parseInt(requestEl.attributeValue("length")),
300                                        0, 0, 
301                                        requestEl.attributeValue("datePattern")==null?null:Long.valueOf(requestEl.attributeValue("datePattern")),
302                                        "",
303                                        createBitSet(requestEl.attributeValue("dates")),
304                                        0);
305                            FreeTimeRequest request = new FreeTimeRequest(
306                                Long.parseLong(requestEl.attributeValue("id")),
307                                Integer.parseInt(requestEl.attributeValue("priority")),
308                                "true".equals(requestEl.attributeValue("alternative")),
309                                student,
310                                time);
311                            if (requestEl.attributeValue("weight")!=null)
312                                request.setWeight(Double.parseDouble(requestEl.attributeValue("weight")));
313                            if (iLoadBest && requestEl.element("best")!=null)
314                                bestEnrollments.add(request.createEnrollment());
315                            if (iLoadInitial && requestEl.element("initial")!=null)
316                                request.setInitialAssignment(request.createEnrollment());
317                            if (iLoadCurrent && requestEl.element("current")!=null)
318                                currentEnrollments.add(request.createEnrollment());
319                        } else if ("course".equals(requestEl.getName())) {
320                            Vector courses = new Vector();
321                            courses.add(courseTable.get(Long.valueOf(requestEl.attributeValue("course"))));
322                            for (Iterator k=requestEl.elementIterator("alternative");k.hasNext();)
323                                courses.add(courseTable.get(Long.valueOf(((Element)k.next()).attributeValue("course"))));
324                            CourseRequest courseRequest = new CourseRequest(
325                                Long.parseLong(requestEl.attributeValue("id")),
326                                Integer.parseInt(requestEl.attributeValue("priority")),
327                                "true".equals(requestEl.attributeValue("alternative")),
328                                student,
329                                courses,
330                                "true".equals(requestEl.attributeValue("waitlist")));
331                            if (requestEl.attributeValue("weight")!=null)
332                                courseRequest.setWeight(Double.parseDouble(requestEl.attributeValue("weight")));
333                            for (Iterator k=requestEl.elementIterator("waitlisted");k.hasNext();) {
334                                Element choiceEl = (Element)k.next();
335                                courseRequest.getWaitlistedChoices().add(new Choice(
336                                    (Offering)offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))),
337                                    choiceEl.getText()));
338                            }
339                            for (Iterator k=requestEl.elementIterator("selected");k.hasNext();) {
340                                Element choiceEl = (Element)k.next();
341                                courseRequest.getSelectedChoices().add(new Choice(
342                                    (Offering)offeringTable.get(Long.valueOf(choiceEl.attributeValue("offering"))),
343                                    choiceEl.getText()));
344                            }
345                            Element initialEl = requestEl.element("initial");
346                            if (iLoadInitial && initialEl!=null) {
347                                HashSet sections = new HashSet();
348                                for (Iterator k=initialEl.elementIterator("section");k.hasNext();) {
349                                    Element sectionEl = (Element)k.next();
350                                    Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id")));
351                                    sections.add(section);
352                                }
353                                if (!sections.isEmpty())
354                                    courseRequest.setInitialAssignment(courseRequest.createEnrollment(sections));
355                            }
356                            Element currentEl = requestEl.element("current");
357                            if (iLoadCurrent && currentEl!=null) {
358                                HashSet sections = new HashSet();
359                                for (Iterator k=currentEl.elementIterator("section");k.hasNext();) {
360                                    Element sectionEl = (Element)k.next();
361                                    Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id")));
362                                    sections.add(section);
363                                }
364                                if (!sections.isEmpty())
365                                    currentEnrollments.add(courseRequest.createEnrollment(sections));
366                            }
367                            Element bestEl = requestEl.element("best");
368                            if (iLoadBest && bestEl!=null) {
369                                HashSet sections = new HashSet();
370                                for (Iterator k=bestEl.elementIterator("section");k.hasNext();) {
371                                    Element sectionEl = (Element)k.next();
372                                    Section section = courseRequest.getSection(Long.parseLong(sectionEl.attributeValue("id")));
373                                    sections.add(section);
374                                }
375                                if (!sections.isEmpty())
376                                    bestEnrollments.add(courseRequest.createEnrollment(sections));
377                            }
378                        }
379                    }
380                    getModel().addStudent(student);
381                }
382    
383                if (!bestEnrollments.isEmpty()) {
384                    for (Enumeration e=bestEnrollments.elements();e.hasMoreElements();) {
385                        Enrollment enrollment = (Enrollment)e.nextElement();
386                        Hashtable conflicts = getModel().conflictConstraints(enrollment);
387                        if (conflicts.isEmpty())
388                            enrollment.variable().assign(0, enrollment);
389                        else {
390                            sLogger.warn("Enrollment "+enrollment+" conflicts with "+conflicts);
391                        }
392                    }
393                    getModel().saveBest();
394                } 
395                
396                if (!currentEnrollments.isEmpty()) {
397                    for (Enumeration e=getModel().variables().elements();e.hasMoreElements();) {
398                        Request request = (Request)e.nextElement();
399                        if (request.getAssignment()!=null)
400                            request.unassign(0);
401                    }
402                    for (Enumeration e=currentEnrollments.elements();e.hasMoreElements();) {
403                        Enrollment enrollment = (Enrollment)e.nextElement();
404                        enrollment.variable().assign(0, enrollment);
405                    }
406                }
407            }
408            
409            sLogger.debug("Model successfully loaded.");
410        }
411        
412            
413    }