001package org.cpsolver.instructor.model;
002
003import java.text.DecimalFormat;
004import java.util.ArrayList;
005import java.util.BitSet;
006import java.util.Collection;
007import java.util.Date;
008import java.util.HashMap;
009import java.util.HashSet;
010import java.util.Iterator;
011import java.util.List;
012import java.util.Map;
013import java.util.Set;
014import java.util.TreeSet;
015
016import org.apache.log4j.Logger;
017import org.cpsolver.coursett.Constants;
018import org.cpsolver.coursett.model.TimeLocation;
019import org.cpsolver.ifs.assignment.Assignment;
020import org.cpsolver.ifs.criteria.Criterion;
021import org.cpsolver.ifs.model.Constraint;
022import org.cpsolver.ifs.model.Model;
023import org.cpsolver.ifs.util.DataProperties;
024import org.cpsolver.ifs.util.ToolBox;
025import org.cpsolver.instructor.constraints.InstructorConstraint;
026import org.cpsolver.instructor.constraints.SameInstructorConstraint;
027import org.cpsolver.instructor.constraints.SameLinkConstraint;
028import org.cpsolver.instructor.criteria.UnusedInstructorLoad;
029import org.cpsolver.instructor.criteria.AttributePreferences;
030import org.cpsolver.instructor.criteria.BackToBack;
031import org.cpsolver.instructor.criteria.CoursePreferences;
032import org.cpsolver.instructor.criteria.InstructorPreferences;
033import org.cpsolver.instructor.criteria.SameInstructor;
034import org.cpsolver.instructor.criteria.DifferentLecture;
035import org.cpsolver.instructor.criteria.OriginalInstructor;
036import org.cpsolver.instructor.criteria.SameCommon;
037import org.cpsolver.instructor.criteria.SameCourse;
038import org.cpsolver.instructor.criteria.SameLink;
039import org.cpsolver.instructor.criteria.TeachingPreferences;
040import org.cpsolver.instructor.criteria.TimeOverlaps;
041import org.cpsolver.instructor.criteria.TimePreferences;
042import org.dom4j.Document;
043import org.dom4j.DocumentHelper;
044import org.dom4j.Element;
045
046/**
047 * Instructor Scheduling Model. Variables are {@link org.cpsolver.instructor.model.TeachingRequest}, values are {@link org.cpsolver.instructor.model.TeachingAssignment}.
048 * Each teaching request has a course (see {@link org.cpsolver.instructor.model.Course}) and one or more sections (see {link {@link org.cpsolver.instructor.model.Section}}).
049 * Each assignment assigns one instructor (see {@link org.cpsolver.instructor.model.Instructor}) to a single teaching request.
050 * 
051 * @version IFS 1.3 (Instructor Sectioning)<br>
052 *          Copyright (C) 2016 Tomas Muller<br>
053 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
054 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
055 * <br>
056 *          This library is free software; you can redistribute it and/or modify
057 *          it under the terms of the GNU Lesser General Public License as
058 *          published by the Free Software Foundation; either version 3 of the
059 *          License, or (at your option) any later version. <br>
060 * <br>
061 *          This library is distributed in the hope that it will be useful, but
062 *          WITHOUT ANY WARRANTY; without even the implied warranty of
063 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
064 *          Lesser General Public License for more details. <br>
065 * <br>
066 *          You should have received a copy of the GNU Lesser General Public
067 *          License along with this library; if not see
068 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
069 */
070public class InstructorSchedulingModel extends Model<TeachingRequest.Variable, TeachingAssignment> {
071    private static Logger sLog = Logger.getLogger(InstructorSchedulingModel.class);
072    private DataProperties iProperties;
073    private Set<Attribute.Type> iTypes = new HashSet<Attribute.Type>();
074    private List<Instructor> iInstructors = new ArrayList<Instructor>();
075    private List<TeachingRequest> iRequests = new ArrayList<TeachingRequest>();
076
077    /**
078     * Constructor
079     * @param properties data properties
080     */
081    public InstructorSchedulingModel(DataProperties properties) {
082        super();
083        iProperties = properties;
084        addCriterion(new AttributePreferences());
085        addCriterion(new InstructorPreferences());
086        addCriterion(new TeachingPreferences());
087        addCriterion(new TimePreferences());
088        addCriterion(new CoursePreferences());
089        addCriterion(new BackToBack());
090        addCriterion(new SameInstructor());
091        addCriterion(new TimeOverlaps());
092        addCriterion(new DifferentLecture());
093        addCriterion(new SameLink());
094        addCriterion(new OriginalInstructor());
095        addCriterion(new UnusedInstructorLoad());
096        addCriterion(new SameCourse());
097        addCriterion(new SameCommon());
098        addGlobalConstraint(new InstructorConstraint());
099    }
100    
101    /**
102     * Return solver configuration
103     * @return data properties given in the constructor
104     */
105    public DataProperties getProperties() {
106        return iProperties;
107    }
108    
109    /**
110     * Add instructor
111     * @param instructor
112     */
113    public void addInstructor(Instructor instructor) {
114        instructor.setModel(this);
115        iInstructors.add(instructor);
116        for (Attribute attribute: instructor.getAttributes())
117            addAttributeType(attribute.getType());
118    }
119    
120    /**
121     * All instructors
122     * @return all instructors in the model
123     */
124    public List<Instructor> getInstructors() {
125        return iInstructors;
126    }
127
128    /**
129     * Add teaching request and the related variables
130     * @param request teaching request
131     */
132    public void addRequest(TeachingRequest request) {
133        iRequests.add(request);
134        for (TeachingRequest.Variable variable: request.getVariables())
135            addVariable(variable);
136    }
137    
138    /**
139     * All teaching requests
140     * @return all teaching requests in the model
141     */
142    public List<TeachingRequest> getRequests() {
143        return iRequests;
144    }
145
146    
147    /**
148     * Return registered attribute types
149     * @return attribute types in the problem
150     */
151    public Set<Attribute.Type> getAttributeTypes() { return iTypes; }
152    
153    /**
154     * Register an attribute type
155     * @param type attribute type
156     */
157    public void addAttributeType(Attribute.Type type) { iTypes.add(type); }
158
159    @Override
160    public Map<String, String> getInfo(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
161        Map<String, String> info = super.getInfo(assignment);
162
163        double totalLoad = 0.0;
164        double assignedLoad = 0.0;
165        for (TeachingRequest.Variable clazz : variables()) {
166            totalLoad += clazz.getRequest().getLoad();
167            if (assignment.getValue(clazz) != null)
168                assignedLoad += clazz.getRequest().getLoad();
169        }
170        info.put("Assigned Load", getPerc(assignedLoad, totalLoad, 0) + "% (" + sDoubleFormat.format(assignedLoad) + " / " + sDoubleFormat.format(totalLoad) + ")");
171
172        return info;
173    }
174
175    @Override
176    public double getTotalValue(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
177        double ret = 0;
178        for (Criterion<TeachingRequest.Variable, TeachingAssignment> criterion : getCriteria())
179            ret += criterion.getWeightedValue(assignment);
180        return ret;
181    }
182
183    @Override
184    public double getTotalValue(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, Collection<TeachingRequest.Variable> variables) {
185        double ret = 0;
186        for (Criterion<TeachingRequest.Variable, TeachingAssignment> criterion : getCriteria())
187            ret += criterion.getWeightedValue(assignment, variables);
188        return ret;
189    }
190    
191    /**
192     * Store the problem (together with its solution) in an XML format
193     * @param assignment current assignment
194     * @return XML document with the problem
195     */
196    public Document save(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
197        DecimalFormat sDF7 = new DecimalFormat("0000000");
198        boolean saveInitial = getProperties().getPropertyBoolean("Xml.SaveInitial", false);
199        boolean saveBest = getProperties().getPropertyBoolean("Xml.SaveBest", false);
200        boolean saveSolution = getProperties().getPropertyBoolean("Xml.SaveSolution", true);
201        Document document = DocumentHelper.createDocument();
202        if (assignment != null && assignment.nrAssignedVariables() > 0) {
203            StringBuffer comments = new StringBuffer("Solution Info:\n");
204            Map<String, String> solutionInfo = (getProperties().getPropertyBoolean("Xml.ExtendedInfo", true) ? getExtendedInfo(assignment) : getInfo(assignment));
205            for (String key : new TreeSet<String>(solutionInfo.keySet())) {
206                String value = solutionInfo.get(key);
207                comments.append("    " + key + ": " + value + "\n");
208            }
209            document.addComment(comments.toString());
210        }
211        Element root = document.addElement("instructor-schedule");
212        root.addAttribute("version", "1.0");
213        root.addAttribute("created", String.valueOf(new Date()));
214        Element typesEl = root.addElement("attributes");
215        for (Attribute.Type type: getAttributeTypes()) {
216            Element typeEl = typesEl.addElement("type");
217            if (type.getTypeId() != null)
218                typeEl.addAttribute("id", String.valueOf(type.getTypeId()));
219            typeEl.addAttribute("name", type.getTypeName());
220            typeEl.addAttribute("conjunctive", type.isConjunctive() ? "true" : "false");
221            typeEl.addAttribute("required", type.isRequired() ? "true": "false");
222            Set<Attribute> attributes = new HashSet<Attribute>();
223            for (TeachingRequest request: getRequests()) {
224                for (Preference<Attribute> pref: request.getAttributePreferences()) {
225                    Attribute attribute = pref.getTarget();
226                    if (type.equals(attribute.getType()) && attributes.add(attribute)) {
227                        Element attributeEl = typeEl.addElement("attribute");
228                        if (attribute.getAttributeId() != null)
229                            attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId()));
230                        attributeEl.addAttribute("name", attribute.getAttributeName());
231                    }
232                }
233                for (Instructor instructor: getInstructors()) {
234                    for (Attribute attribute: instructor.getAttributes()) {
235                        if (type.equals(attribute.getType()) && attributes.add(attribute)) {
236                            Element attributeEl = typeEl.addElement("attribute");
237                            if (attribute.getAttributeId() != null)
238                                attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId()));
239                            attributeEl.addAttribute("name", attribute.getAttributeName());
240                        }
241                    }
242                }
243            }
244        }
245        Element requestsEl = root.addElement("teaching-requests");
246        for (TeachingRequest request: getRequests()) {
247            Element requestEl = requestsEl.addElement("request");
248            requestEl.addAttribute("id", String.valueOf(request.getRequestId()));
249            if (request.getNrInstructors() != 1)
250                requestEl.addAttribute("nrInstructors", String.valueOf(request.getNrInstructors()));
251            Course course = request.getCourse();
252            Element courseEl = requestEl.addElement("course");
253            if (course.getCourseId() != null)
254                courseEl.addAttribute("id", String.valueOf(course.getCourseId()));
255            if (course.getCourseName() != null)
256                courseEl.addAttribute("name", String.valueOf(course.getCourseName()));
257            for (Section section: request.getSections()) {
258                Element sectionEl = requestEl.addElement("section");
259                sectionEl.addAttribute("id", String.valueOf(section.getSectionId()));
260                if (section.getExternalId() != null && !section.getExternalId().isEmpty()) sectionEl.addAttribute("externalId", section.getExternalId());
261                if (section.getSectionType() != null && !section.getSectionType().isEmpty()) sectionEl.addAttribute("type", section.getSectionType());
262                if (section.getSectionName() != null && !section.getSectionName().isEmpty()) sectionEl.addAttribute("name", section.getSectionName());
263                if (section.hasTime()) {
264                    TimeLocation tl = section.getTime();
265                    Element timeEl = sectionEl.addElement("time");
266                    timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))));
267                    timeEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
268                    timeEl.addAttribute("length", String.valueOf(tl.getLength()));
269                    if (tl.getBreakTime() != 0)
270                        timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
271                    if (tl.getTimePatternId() != null)
272                        timeEl.addAttribute("pattern", tl.getTimePatternId().toString());
273                    if (tl.getDatePatternId() != null)
274                        timeEl.addAttribute("datePattern", tl.getDatePatternId().toString());
275                    if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty())
276                        timeEl.addAttribute("datePatternName", tl.getDatePatternName());
277                    if (tl.getWeekCode() != null)
278                        timeEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
279                    timeEl.setText(tl.getLongName(false));
280                }
281                if (section.hasRoom()) sectionEl.addAttribute("room", section.getRoom());
282                if (section.isAllowOverlap()) sectionEl.addAttribute("canOverlap", "true");
283                if (section.isCommon()) sectionEl.addAttribute("common", "true");
284            }
285            requestEl.addAttribute("load", sDoubleFormat.format(request.getLoad()));
286            requestEl.addAttribute("sameCourse", Constants.preferenceLevel2preference(request.getSameCoursePreference()));
287            requestEl.addAttribute("sameCommon", Constants.preferenceLevel2preference(request.getSameCommonPreference()));
288            for (Preference<Attribute> pref: request.getAttributePreferences()) {
289                Element attributeEl = requestEl.addElement("attribute");
290                if (pref.getTarget().getAttributeId() != null)
291                    attributeEl.addAttribute("id", String.valueOf(pref.getTarget().getAttributeId()));
292                attributeEl.addAttribute("name", pref.getTarget().getAttributeName());
293                attributeEl.addAttribute("type", pref.getTarget().getType().getTypeName());
294                attributeEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference())));
295            }
296            for (Preference<Instructor> pref: request.getInstructorPreferences()) {
297                Element instructorEl = requestEl.addElement("instructor");
298                instructorEl.addAttribute("id", String.valueOf(pref.getTarget().getInstructorId()));
299                if (pref.getTarget().hasExternalId())
300                    instructorEl.addAttribute("externalId", pref.getTarget().getExternalId());
301                if (pref.getTarget().hasName())
302                    instructorEl.addAttribute("name", pref.getTarget().getName());
303                instructorEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference())));
304            }
305            if (saveBest)
306                for (TeachingRequest.Variable variable: request.getVariables()) {
307                    if (variable.getBestAssignment() != null) {
308                        Instructor instructor = variable.getBestAssignment().getInstructor();
309                        Element instructorEl = requestEl.addElement("best-instructor");
310                        instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
311                        if (request.getNrInstructors() != 1)
312                            instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex()));
313                        if (instructor.hasExternalId())
314                            instructorEl.addAttribute("externalId", instructor.getExternalId());
315                        if (instructor.hasName())
316                            instructorEl.addAttribute("name", instructor.getName());
317                    }                    
318                }
319            if (saveInitial)
320                for (TeachingRequest.Variable variable: request.getVariables()) {
321                    if (variable.getInitialAssignment() != null) {
322                        Instructor instructor = variable.getInitialAssignment().getInstructor();
323                        Element instructorEl = requestEl.addElement("initial-instructor");
324                        instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
325                        if (request.getNrInstructors() != 1)
326                            instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex()));
327                        if (instructor.hasExternalId())
328                            instructorEl.addAttribute("externalId", instructor.getExternalId());
329                        if (instructor.hasName())
330                            instructorEl.addAttribute("name", instructor.getName());
331                    }
332                }
333            if (saveSolution)
334                for (TeachingRequest.Variable variable: request.getVariables()) {
335                    TeachingAssignment ta = assignment.getValue(variable);
336                    if (ta != null) {
337                        Instructor instructor = ta.getInstructor();
338                        Element instructorEl = requestEl.addElement("assigned-instructor");
339                        instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
340                        if (request.getNrInstructors() != 1)
341                            instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex()));
342                        if (instructor.hasExternalId())
343                            instructorEl.addAttribute("externalId", instructor.getExternalId());
344                        if (instructor.hasName())
345                            instructorEl.addAttribute("name", instructor.getName());
346                    }
347                }
348        }
349        Element instructorsEl = root.addElement("instructors");
350        for (Instructor instructor: getInstructors()) {
351            Element instructorEl = instructorsEl.addElement("instructor");
352            instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
353            if (instructor.hasExternalId())
354                instructorEl.addAttribute("externalId", instructor.getExternalId());
355            if (instructor.hasName())
356                instructorEl.addAttribute("name", instructor.getName());
357            if (instructor.getPreference() != 0)
358                instructorEl.addAttribute("preference", String.valueOf(instructor.getPreference()));
359            if (instructor.getBackToBackPreference() != 0)
360                instructorEl.addAttribute("btb", String.valueOf(instructor.getBackToBackPreference()));
361            for (Attribute attribute: instructor.getAttributes()) {
362                Element attributeEl = instructorEl.addElement("attribute");
363                if (attribute.getAttributeId() != null)
364                    attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId()));
365                attributeEl.addAttribute("name", attribute.getAttributeName());
366                attributeEl.addAttribute("type", attribute.getType().getTypeName());
367            }
368            instructorEl.addAttribute("maxLoad", sDoubleFormat.format(instructor.getMaxLoad()));
369            for (Preference<TimeLocation> tp: instructor.getTimePreferences()) {
370                
371                Element timeEl = instructorEl.addElement("time");
372                TimeLocation tl = tp.getTarget();
373                timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))));
374                timeEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
375                timeEl.addAttribute("length", String.valueOf(tl.getLength()));
376                if (tl.getBreakTime() != 0)
377                    timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
378                if (tl.getTimePatternId() != null)
379                    timeEl.addAttribute("pattern", tl.getTimePatternId().toString());
380                if (tl.getDatePatternId() != null)
381                    timeEl.addAttribute("datePattern", tl.getDatePatternId().toString());
382                if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty())
383                    timeEl.addAttribute("datePatternName", tl.getDatePatternName());
384                if (tl.getWeekCode() != null)
385                    timeEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
386                timeEl.addAttribute("preference", tp.isProhibited() ? "P" : tp.isRequired() ? "R" : String.valueOf(tp.getPreference()));
387                if (tp.getTarget() instanceof EnrolledClass) {
388                    Element classEl = timeEl.addElement("section");
389                    Element courseEl = null;
390                    EnrolledClass ec = (EnrolledClass)tp.getTarget();
391                    if (ec.getCourseId() != null || ec.getCourse() != null) {
392                        courseEl = timeEl.addElement("course");
393                        if (ec.getCourseId() != null) courseEl.addAttribute("id", String.valueOf(ec.getCourseId()));
394                        if (ec.getCourse() != null) courseEl.addAttribute("name", ec.getCourse());
395                    }
396                    if (ec.getClassId() != null) classEl.addAttribute("id", String.valueOf(ec.getClassId()));
397                    if (ec.getType() != null) classEl.addAttribute("type", ec.getType());
398                    if (ec.getSection() != null) classEl.addAttribute("name", ec.getSection());
399                    if (ec.getExternalId() != null) classEl.addAttribute("externalId", ec.getExternalId());
400                    if (ec.getRoom() != null) classEl.addAttribute("room", ec.getRoom());
401                    classEl.addAttribute("role", ec.isInstructor() ? "instructor" : "student");
402                } else {
403                    timeEl.setText(tl.getLongName(false));
404                }
405            }
406            for (Preference<Course> cp: instructor.getCoursePreferences()) {
407                Element courseEl = instructorEl.addElement("course");
408                Course course = cp.getTarget();
409                if (course.getCourseId() != null)
410                    courseEl.addAttribute("id", String.valueOf(course.getCourseId()));
411                if (course.getCourseName() != null)
412                    courseEl.addAttribute("name", String.valueOf(course.getCourseName()));
413                courseEl.addAttribute("preference", cp.isProhibited() ? "P" : cp.isRequired() ? "R" : String.valueOf(cp.getPreference()));
414            }
415        }
416        Element constraintsEl = root.addElement("constraints");
417        for (Constraint<TeachingRequest.Variable, TeachingAssignment> c: constraints()) {
418            if (c instanceof SameInstructorConstraint) {
419                SameInstructorConstraint si = (SameInstructorConstraint) c;
420                Element sameInstEl = constraintsEl.addElement("same-instructor");
421                if (si.getConstraintId() != null)
422                    sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId()));
423                if (si.getName() != null)
424                    sameInstEl.addAttribute("name", si.getName());
425                sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference()));
426                for (TeachingRequest.Variable request: c.variables()) {
427                    Element assignmentEl = sameInstEl.addElement("request");
428                    assignmentEl.addAttribute("id", String.valueOf(request.getRequest().getRequestId()));
429                    if (request.getRequest().getNrInstructors() != 1)
430                        assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex()));
431                }
432            } else if (c instanceof SameLinkConstraint) {
433                SameLinkConstraint si = (SameLinkConstraint) c;
434                Element sameInstEl = constraintsEl.addElement("same-link");
435                if (si.getConstraintId() != null)
436                    sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId()));
437                if (si.getName() != null)
438                    sameInstEl.addAttribute("name", si.getName());
439                sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference()));
440                for (TeachingRequest.Variable request: c.variables()) {
441                    Element assignmentEl = sameInstEl.addElement("request");
442                    assignmentEl.addAttribute("id", String.valueOf(request.getRequest().getRequestId()));
443                    if (request.getRequest().getNrInstructors() != 1)
444                        assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex()));
445                }
446            }
447        }
448        return document;
449    }
450    
451    /**
452     * Load the problem (and its solution) from an XML format
453     * @param document XML document
454     * @param assignment current assignment
455     * @return true, if the problem was successfully loaded in
456     */
457    public boolean load(Document document, Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
458        boolean loadInitial = getProperties().getPropertyBoolean("Xml.LoadInitial", true);
459        boolean loadBest = getProperties().getPropertyBoolean("Xml.LoadBest", true);
460        boolean loadSolution = getProperties().getPropertyBoolean("Xml.LoadSolution", true);
461        String defaultBtb = getProperties().getProperty("Defaults.BackToBack", "0");
462        String defaultConjunctive = getProperties().getProperty("Defaults.Conjunctive", "false");
463        String defaultRequired = getProperties().getProperty("Defaults.Required", "false");
464        String defaultSameCourse = getProperties().getProperty("Defaults.SameCourse", "R");
465        String defaultSameCommon = getProperties().getProperty("Defaults.SameCommon", "R");
466        Element root = document.getRootElement();
467        if (!"instructor-schedule".equals(root.getName()))
468            return false;
469        Map<String, Attribute.Type> types = new HashMap<String, Attribute.Type>();
470        Map<Long, Attribute> attributes = new HashMap<Long, Attribute>();
471        if (root.element("attributes") != null) {
472            for (Iterator<?> i = root.element("attributes").elementIterator("type"); i.hasNext();) {
473                Element typeEl = (Element) i.next();
474                Attribute.Type type = new Attribute.Type(
475                        Long.parseLong(typeEl.attributeValue("id")),
476                        typeEl.attributeValue("name"),
477                        "true".equalsIgnoreCase(typeEl.attributeValue("conjunctive", defaultConjunctive)),
478                        "true".equalsIgnoreCase(typeEl.attributeValue("required", defaultRequired)));
479                addAttributeType(type);
480                if (type.getTypeName() != null)
481                    types.put(type.getTypeName(), type);
482                for (Iterator<?> j = typeEl.elementIterator("attribute"); j.hasNext();) {
483                    Element attributeEl = (Element) j.next();
484                    Attribute attribute = new Attribute(
485                            Long.parseLong(attributeEl.attributeValue("id")),
486                            attributeEl.attributeValue("name"),
487                            type);
488                    attributes.put(attribute.getAttributeId(), attribute);
489                }
490            }
491        }
492        Map<Long, Course> courses = new HashMap<Long, Course>();
493        if (root.element("courses") != null) {
494            for (Iterator<?> i = root.element("courses").elementIterator("course"); i.hasNext();) {
495                Element courseEl = (Element) i.next();
496                Course course = new Course(
497                        Long.parseLong(courseEl.attributeValue("id")),
498                        courseEl.attributeValue("name"));
499                courses.put(course.getCourseId(), course);
500            }
501        }
502        Map<Long, Instructor> instructors = new HashMap<Long, Instructor>();
503        for (Iterator<?> i = root.element("instructors").elementIterator("instructor"); i.hasNext();) {
504            Element instructorEl = (Element) i.next();
505            Instructor instructor = new Instructor(
506                    Long.parseLong(instructorEl.attributeValue("id")),
507                    instructorEl.attributeValue("externalId"),
508                    instructorEl.attributeValue("name"),
509                    string2preference(instructorEl.attributeValue("preference")),
510                    Float.parseFloat(instructorEl.attributeValue("maxLoad", "0")));
511            instructor.setBackToBackPreference(Integer.valueOf(instructorEl.attributeValue("btb", defaultBtb)));
512            for (Iterator<?> j = instructorEl.elementIterator("attribute"); j.hasNext();) {
513                Element f = (Element) j.next();
514                Long attributeId = Long.valueOf(f.attributeValue("id"));
515                Attribute attribute = attributes.get(attributeId);
516                if (attribute == null) {
517                    Attribute.Type type = types.get(f.attributeValue("type"));
518                    if (type == null) {
519                        type = new Attribute.Type(types.size(), f.attributeValue("type"),
520                                "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)),
521                                "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired)));
522                        types.put(type.getTypeName(), type);
523                    }
524                    attribute = new Attribute(attributeId, f.attributeValue("name"), type);
525                    attributes.put(attributeId, attribute);
526                }
527                instructor.addAttribute(attribute);
528            }
529            for (Iterator<?> j = instructorEl.elementIterator("time"); j.hasNext();) {
530                Element f = (Element) j.next();
531                Element classEl = f.element("section");
532                Element courseEl = f.element("course");
533                TimeLocation time = null;
534                if (classEl != null) {
535                    time = new EnrolledClass(
536                            courseEl == null || courseEl.attributeValue("id") == null ? null : Long.valueOf(courseEl.attributeValue("id")),
537                            classEl.attributeValue("id") == null ? null : Long.valueOf(classEl.attributeValue("id")),
538                            courseEl == null ? null : courseEl.attributeValue("name"),
539                            classEl.attributeValue("type"),
540                            classEl.attributeValue("name"),
541                            classEl.attributeValue("externalId"),
542                            Integer.parseInt(f.attributeValue("days"), 2),
543                            Integer.parseInt(f.attributeValue("start")),
544                            Integer.parseInt(f.attributeValue("length")),
545                            f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")),
546                            f.attributeValue("datePatternName", ""),
547                            createBitSet(f.attributeValue("dates")),
548                            Integer.parseInt(f.attributeValue("breakTime", "0")),
549                            classEl.attributeValue("room"),
550                            "instructor".equalsIgnoreCase(classEl.attributeValue("role", "instructor")));
551                } else {
552                    time = new TimeLocation(
553                            Integer.parseInt(f.attributeValue("days"), 2),
554                            Integer.parseInt(f.attributeValue("start")),
555                            Integer.parseInt(f.attributeValue("length")), 0, 0,
556                            f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")),
557                            f.attributeValue("datePatternName", ""),
558                            createBitSet(f.attributeValue("dates")),
559                            Integer.parseInt(f.attributeValue("breakTime", "0")));
560                }
561                if (f.attributeValue("pattern") != null)
562                    time.setTimePatternId(Long.valueOf(f.attributeValue("pattern")));
563                instructor.addTimePreference(new Preference<TimeLocation>(time, string2preference(f.attributeValue("preference"))));
564            }
565            for (Iterator<?> j = instructorEl.elementIterator("course"); j.hasNext();) {
566                Element f = (Element) j.next();
567                instructor.addCoursePreference(new Preference<Course>(new Course(Long.parseLong(f.attributeValue("id")), f.attributeValue("name")), string2preference(f.attributeValue("preference"))));
568            }
569            addInstructor(instructor);
570            instructors.put(instructor.getInstructorId(), instructor);
571        }
572        Map<Long, TeachingRequest> requests = new HashMap<Long, TeachingRequest>();
573        Map<TeachingRequest, Map<Integer, Instructor>> current = new HashMap<TeachingRequest, Map<Integer, Instructor>>();
574        Map<TeachingRequest, Map<Integer, Instructor>> best = new HashMap<TeachingRequest, Map<Integer, Instructor>>();
575        Map<TeachingRequest, Map<Integer, Instructor>> initial = new HashMap<TeachingRequest, Map<Integer, Instructor>>();
576        for (Iterator<?> i = root.element("teaching-requests").elementIterator("request"); i.hasNext();) {
577            Element requestEl = (Element) i.next();
578            Element courseEl = requestEl.element("course");
579            Course course = null;
580            if (courseEl != null) {
581                Long courseId = Long.valueOf(courseEl.attributeValue("id"));
582                course = courses.get(courseId);
583                if (course == null) {
584                    course = new Course(courseId, courseEl.attributeValue("name"));
585                }
586            } else {
587                course = courses.get(Long.valueOf(requestEl.attributeValue("course")));
588            }
589            List<Section> sections = new ArrayList<Section>();
590            for (Iterator<?> j = requestEl.elementIterator("section"); j.hasNext();) {
591                Element f = (Element) j.next();
592                TimeLocation time = null;
593                Element timeEl = f.element("time");
594                if (timeEl != null) {
595                    time = new TimeLocation(
596                            Integer.parseInt(timeEl.attributeValue("days"), 2),
597                            Integer.parseInt(timeEl.attributeValue("start")),
598                            Integer.parseInt(timeEl.attributeValue("length")), 0, 0,
599                            timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")),
600                            timeEl.attributeValue("datePatternName", ""),
601                            createBitSet(timeEl.attributeValue("dates")),
602                            Integer.parseInt(timeEl.attributeValue("breakTime", "0")));
603                    if (timeEl.attributeValue("pattern") != null)
604                        time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
605                }
606                Section section = new Section(
607                        Long.valueOf(f.attributeValue("id")),
608                        f.attributeValue("externalId"),
609                        f.attributeValue("type"),
610                        f.attributeValue("name"),
611                        time,
612                        f.attributeValue("room"),
613                        "true".equalsIgnoreCase(f.attributeValue("canOverlap", "false")),
614                        "true".equalsIgnoreCase(f.attributeValue("common", "false")));
615                sections.add(section);
616            }
617            TeachingRequest request = new TeachingRequest(
618                    Long.parseLong(requestEl.attributeValue("id")),
619                    Integer.parseInt(requestEl.attributeValue("nrInstructors", "1")),
620                    course,
621                    Float.valueOf(requestEl.attributeValue("load", "0")),
622                    sections,
623                    Constants.preference2preferenceLevel(requestEl.attributeValue("sameCourse", defaultSameCourse)),
624                    Constants.preference2preferenceLevel(requestEl.attributeValue("sameCommon", defaultSameCommon)));
625            requests.put(request.getRequestId(), request);
626            for (Iterator<?> j = requestEl.elementIterator("attribute"); j.hasNext();) {
627                Element f = (Element) j.next();
628                Long attributeId = Long.valueOf(f.attributeValue("id"));
629                Attribute attribute = attributes.get(attributeId);
630                if (attribute == null) {
631                    Attribute.Type type = types.get(f.attributeValue("type"));
632                    if (type == null) {
633                        type = new Attribute.Type(types.size(), f.attributeValue("type"),
634                                "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)),
635                                "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired)));
636                        types.put(type.getTypeName(), type);
637                    }
638                    attribute = new Attribute(attributeId, f.attributeValue("name"), type);
639                    attributes.put(attributeId, attribute);
640                }
641                request.addAttributePreference(new Preference<Attribute>(attribute, string2preference(f.attributeValue("preference"))));
642            }
643            for (Iterator<?> j = requestEl.elementIterator("instructor"); j.hasNext();) {
644                Element f = (Element) j.next();
645                Long instructorId = Long.valueOf(f.attributeValue("id"));
646                Instructor instructor = instructors.get(instructorId);
647                if (instructor != null)
648                    request.addInstructorPreference(new Preference<Instructor>(instructor, string2preference(f.attributeValue("preference"))));
649            }
650            if (loadBest) {
651                for (Iterator<?> j = requestEl.elementIterator("best-instructor"); j.hasNext();) {
652                    Element f = (Element) j.next();
653                    Map<Integer, Instructor> idx2inst = best.get(request);
654                    if (idx2inst == null) {
655                        idx2inst = new HashMap<Integer, Instructor>();
656                        best.put(request, idx2inst);
657                    }
658                    int index = 1 + Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size())));
659                    Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id")));
660                    if (instructor != null)
661                        idx2inst.put(index, instructor);
662                }
663            }
664            if (loadInitial) {
665                for (Iterator<?> j = requestEl.elementIterator("initial-instructor"); j.hasNext();) {
666                    Element f = (Element) j.next();
667                    Map<Integer, Instructor> idx2inst = initial.get(request);
668                    if (idx2inst == null) {
669                        idx2inst = new HashMap<Integer, Instructor>();
670                        initial.put(request, idx2inst);
671                    }
672                    int index = 1 + Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size())));
673                    Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id")));
674                    if (instructor != null)
675                        idx2inst.put(index, instructor);
676                }
677            }
678            if (loadSolution) {
679                for (Iterator<?> j = requestEl.elementIterator("assigned-instructor"); j.hasNext();) {
680                    Element f = (Element) j.next();
681                    Map<Integer, Instructor> idx2inst = current.get(request);
682                    if (idx2inst == null) {
683                        idx2inst = new HashMap<Integer, Instructor>();
684                        current.put(request, idx2inst);
685                    }
686                    int index = Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size())));
687                    Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id")));
688                    if (instructor != null)
689                        idx2inst.put(index, instructor);
690                }
691            }
692            addRequest(request);
693        }
694        if (root.element("constraints") != null) {
695            for (Iterator<?> i = root.element("constraints").elementIterator(); i.hasNext();) {
696                Element constraintEl = (Element) i.next();
697                Constraint<TeachingRequest.Variable, TeachingAssignment> constraint = null;
698                if ("same-link".equals(constraintEl.getName())) {
699                    constraint = new SameLinkConstraint(
700                            (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))),
701                            constraintEl.attributeValue("name"),
702                            constraintEl.attributeValue("preference"));
703                } else if ("same-instructor".equals(constraintEl.getName())) {
704                    constraint = new SameInstructorConstraint(
705                            (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))),
706                            constraintEl.attributeValue("name"),
707                            constraintEl.attributeValue("preference"));
708                }
709                if (constraint != null) {
710                    for (Iterator<?> j = constraintEl.elementIterator("request"); j.hasNext();) {
711                        Element f = (Element) j.next();
712                        TeachingRequest request = requests.get(Long.valueOf(f.attributeValue("id")));
713                        if (request != null) {
714                            int index = Integer.valueOf(f.attributeValue("index", "0"));
715                            if (index >= 0 && index < request.getNrInstructors())
716                                constraint.addVariable(request.getVariables()[index]);
717                        }
718                    }
719                    addConstraint(constraint);
720                }
721            }            
722        }
723        for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: best.entrySet())
724            for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet())
725                if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) {
726                    TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()];
727                    variable.setBestAssignment(new TeachingAssignment(variable, e2.getValue()), 0l);
728                }
729
730        for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: initial.entrySet())
731            for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet())
732                if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) {
733                    TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()];
734                    variable.setInitialAssignment(new TeachingAssignment(variable, e2.getValue()));
735                }
736        
737        if (!current.isEmpty()) {
738            for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: current.entrySet())
739                for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet())
740                    if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) {
741                        TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()];
742                        TeachingAssignment ta = new TeachingAssignment(variable, e2.getValue());
743                        Set<TeachingAssignment> conf = conflictValues(assignment, ta);
744                        if (conf.isEmpty()) {
745                            assignment.assign(0, ta);
746                        } else {
747                            sLog.error("Unable to assign " + ta.getName() + " to " + variable.getName());
748                            sLog.error("Conflicts:" + ToolBox.dict2string(conflictConstraints(assignment, ta), 2));
749                        }
750                    }
751        }
752        
753        return true;
754    }
755    
756    /** Convert bitset to a bit string */
757    protected static String bitset2string(BitSet b) {
758        StringBuffer sb = new StringBuffer();
759        for (int i = 0; i < b.length(); i++)
760            sb.append(b.get(i) ? "1" : "0");
761        return sb.toString();
762    }
763
764    /** Create BitSet from a bit string */
765    protected static BitSet createBitSet(String bitString) {
766        if (bitString == null) return null;
767        BitSet ret = new BitSet(bitString.length());
768        for (int i = 0; i < bitString.length(); i++)
769            if (bitString.charAt(i) == '1')
770                ret.set(i);
771        return ret;
772    }
773    
774    /** Convert preference string to a preference value */
775    protected static int string2preference(String pref) {
776        if (pref == null || pref.isEmpty()) return 0;
777        if (Constants.sPreferenceRequired.equals(pref))
778            return Constants.sPreferenceLevelRequired;
779        if (Constants.sPreferenceProhibited.equals(pref))
780            return Constants.sPreferenceLevelProhibited;
781        return Integer.valueOf(pref);
782    }
783}