001    package net.sf.cpsolver.exam.model;
002    
003    import java.util.ArrayList;
004    import java.util.HashSet;
005    import java.util.List;
006    import java.util.Set;
007    
008    import net.sf.cpsolver.ifs.model.Constraint;
009    
010    /**
011     * An instructor. <br>
012     * <br>
013     * 
014     * @version ExamTT 1.2 (Examination Timetabling)<br>
015     *          Copyright (C) 2008 - 2010 Tomas Muller<br>
016     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
017     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
018     * <br>
019     *          This library is free software; you can redistribute it and/or modify
020     *          it under the terms of the GNU Lesser General Public License as
021     *          published by the Free Software Foundation; either version 3 of the
022     *          License, or (at your option) any later version. <br>
023     * <br>
024     *          This library is distributed in the hope that it will be useful, but
025     *          WITHOUT ANY WARRANTY; without even the implied warranty of
026     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
027     *          Lesser General Public License for more details. <br>
028     * <br>
029     *          You should have received a copy of the GNU Lesser General Public
030     *          License along with this library; if not see
031     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
032     */
033    public class ExamInstructor extends Constraint<Exam, ExamPlacement> {
034        private Set<Exam>[] iTable;
035        private Set<Exam>[] iDayTable;
036        private String iName;
037        private boolean iAllowDirectConflicts = true;
038        private List<ExamOwner> iOwners = new ArrayList<ExamOwner>();
039        private boolean[] iAvailable = null;
040    
041        @SuppressWarnings("unchecked")
042        public ExamInstructor(ExamModel model, long id, String name) {
043            super();
044            iAllowDirectConflicts = model.getProperties().getPropertyBoolean("Instructor.AllowDirectConflicts",
045                    iAllowDirectConflicts);
046            iAssignedVariables = null;
047            iId = id;
048            iName = name;
049            iTable = new Set[model.getNrPeriods()];
050            for (int i = 0; i < iTable.length; i++)
051                iTable[i] = new HashSet<Exam>();
052            iDayTable = new Set[model.getNrDays()];
053            for (int i = 0; i < iDayTable.length; i++)
054                iDayTable[i] = new HashSet<Exam>();
055        }
056    
057        /**
058         * True when direct instructor conflicts are not allowed.
059         */
060        public boolean isAllowDirectConflicts() {
061            return iAllowDirectConflicts;
062        }
063    
064        /**
065         * Set to true when direct instructor conflicts are not allowed.
066         */
067        public void setAllowDirectConflicts(boolean allowDirectConflicts) {
068            iAllowDirectConflicts = allowDirectConflicts;
069        }
070    
071        /**
072         * Exam(s) enrolled by the instructor that are scheduled in the given period
073         */
074        public Set<Exam> getExams(ExamPeriod period) {
075            return iTable[period.getIndex()];
076        }
077    
078        /**
079         * Exam(s) enrolled by the instructor that are scheduled in the given day
080         */
081        public Set<Exam> getExamsADay(ExamPeriod period) {
082            return iDayTable[period.getDay()];
083        }
084    
085        /**
086         * Exam(s) enrolled by the instructor that are scheduled in the given day
087         */
088        public Set<Exam> getExamsADay(int day) {
089            return iDayTable[day];
090        }
091    
092        /**
093         * Compute conflicts between the given assignment of an exam and all the
094         * current assignments (of this instructor). Only not-allowed conflicts (see
095         * {@link ExamInstructor#isAllowDirectConflicts()}) are considered.
096         */
097        @Override
098        public void computeConflicts(ExamPlacement p, Set<ExamPlacement> conflicts) {
099            if (isAllowDirectConflicts())
100                return;
101            for (Exam exam : iTable[p.getPeriod().getIndex()]) {
102                conflicts.add(exam.getAssignment());
103            }
104        }
105    
106        /**
107         * Check whether there is a conflict between the given assignment of an exam
108         * and all the current assignments (of this instructor). Only not-allowed
109         * conflicts (see {@link ExamInstructor#isAllowDirectConflicts()}) are
110         * considered.
111         */
112        @Override
113        public boolean inConflict(ExamPlacement p) {
114            if (isAllowDirectConflicts())
115                return false;
116            return !iTable[p.getPeriod().getIndex()].isEmpty();
117        }
118    
119        /**
120         * True if the given exams can conflict (see
121         * {@link ExamInstructor#isAllowDirectConflicts()}), or if they are placed
122         * at different periods.
123         */
124        @Override
125        public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) {
126            if (isAllowDirectConflicts())
127                return true;
128            return (p1.getPeriod() != p2.getPeriod());
129        }
130    
131        /**
132         * An exam was assigned, update instructor assignment table
133         */
134        public void afterAssigned(long iteration, ExamPlacement p) {
135            iTable[p.getPeriod().getIndex()].add(p.variable());
136            iDayTable[p.getPeriod().getDay()].add(p.variable());
137        }
138    
139        /**
140         * An exam was unassigned, update instructor assignment table
141         */
142        public void afterUnassigned(long iteration, ExamPlacement p) {
143            iTable[p.getPeriod().getIndex()].remove(p.variable());
144            iDayTable[p.getPeriod().getDay()].remove(p.variable());
145        }
146    
147        /**
148         * Compare two instructors for equality
149         */
150        @Override
151        public boolean equals(Object o) {
152            if (o == null || !(o instanceof ExamInstructor))
153                return false;
154            ExamInstructor s = (ExamInstructor) o;
155            return getId() == s.getId();
156        }
157    
158        /**
159         * Hash code
160         */
161        @Override
162        public int hashCode() {
163            return (int) (getId() ^ (getId() >>> 32));
164        }
165    
166        /**
167         * Instructor name
168         */
169        @Override
170        public String getName() {
171            return hasName() ? iName : String.valueOf(getId());
172        }
173    
174        /**
175         * Instructor name
176         */
177        public boolean hasName() {
178            return (iName != null && iName.length() > 0);
179        }
180    
181        /**
182         * Instructor name
183         */
184        @Override
185        public String toString() {
186            return getName();
187        }
188    
189        /**
190         * Compare two instructors (by instructor ids)
191         */
192        public int compareTo(ExamInstructor o) {
193            return toString().compareTo(o.toString());
194        }
195    
196        /**
197         * Courses and/or sections that this instructor is enrolled to
198         * 
199         * @return list of {@link ExamOwner}
200         */
201        public List<ExamOwner> getOwners() {
202            return iOwners;
203        }
204    
205        @Override
206        public boolean isHard() {
207            return !isAllowDirectConflicts();
208        }
209    
210        /**
211         * True if the student is available (for examination timetabling) during the
212         * given period
213         * 
214         * @param period
215         *            a period
216         * @return true if a student can attend an exam at the given period, false
217         *         if otherwise
218         */
219        public boolean isAvailable(ExamPeriod period) {
220            return (iAvailable == null ? true : iAvailable[period.getIndex()]);
221        }
222    
223        /**
224         * Set whether the student is available (for examination timetabling) during
225         * the given period
226         * 
227         * @param period
228         *            a period
229         * @param available
230         *            true if a student can attend an exam at the given period,
231         *            false if otherwise
232         */
233        public void setAvailable(int period, boolean available) {
234            if (iAvailable == null) {
235                iAvailable = new boolean[iTable.length];
236                for (int i = 0; i < iTable.length; i++)
237                    iAvailable[i] = true;
238            }
239            iAvailable[period] = available;
240        }
241    }