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