001    package net.sf.cpsolver.coursett.model;
002    
003    import java.util.ArrayList;
004    import java.util.List;
005    
006    import net.sf.cpsolver.coursett.Constants;
007    import net.sf.cpsolver.coursett.constraint.GroupConstraint;
008    import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
009    import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
010    import net.sf.cpsolver.coursett.preference.PreferenceCombination;
011    import net.sf.cpsolver.ifs.criteria.Criterion;
012    import net.sf.cpsolver.ifs.model.Value;
013    import net.sf.cpsolver.ifs.util.DistanceMetric;
014    import net.sf.cpsolver.ifs.util.ToolBox;
015    
016    /**
017     * Placement (value). <br>
018     * <br>
019     * It combines room and time location
020     * 
021     * @version CourseTT 1.2 (University Course Timetabling)<br>
022     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
023     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
024     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
025     * <br>
026     *          This library is free software; you can redistribute it and/or modify
027     *          it under the terms of the GNU Lesser General Public License as
028     *          published by the Free Software Foundation; either version 3 of the
029     *          License, or (at your option) any later version. <br>
030     * <br>
031     *          This library is distributed in the hope that it will be useful, but
032     *          WITHOUT ANY WARRANTY; without even the implied warranty of
033     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
034     *          Lesser General Public License for more details. <br>
035     * <br>
036     *          You should have received a copy of the GNU Lesser General Public
037     *          License along with this library; if not see
038     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
039     */
040    
041    public class Placement extends Value<Lecture, Placement> {
042        private TimeLocation iTimeLocation;
043        private RoomLocation iRoomLocation;
044        private List<RoomLocation> iRoomLocations = null;
045        private Long iAssignmentId = null;
046    
047            private int iHashCode = 0;
048    
049        /**
050         * Constructor
051         * 
052         * @param lecture
053         *            lecture
054         * @param timeLocation
055         *            time location
056         * @param roomLocation
057         *            room location
058         */
059        public Placement(Lecture lecture, TimeLocation timeLocation, RoomLocation roomLocation) {
060            super(lecture);
061            iTimeLocation = timeLocation;
062            iRoomLocation = roomLocation;
063            if (iRoomLocation == null) {
064                iRoomLocations = new ArrayList<RoomLocation>(0);
065            }
066            iHashCode = getName().hashCode();
067        }
068    
069        public Placement(Lecture lecture, TimeLocation timeLocation, java.util.List<RoomLocation> roomLocations) {
070            super(lecture);
071            iTimeLocation = timeLocation;
072            iRoomLocation = (roomLocations.isEmpty() ? null : (RoomLocation) roomLocations.get(0));
073            if (roomLocations.size() != 1) {
074                iRoomLocations = new ArrayList<RoomLocation>(roomLocations);
075            }
076            iHashCode = getName().hashCode();
077        }
078    
079        /** Time location */
080        public TimeLocation getTimeLocation() {
081            return iTimeLocation;
082        }
083    
084        /** Room location */
085        public RoomLocation getRoomLocation() {
086            return iRoomLocation;
087        }
088    
089        /** Room locations (multi-room placement) */
090        public List<RoomLocation> getRoomLocations() {
091            return iRoomLocations;
092        }
093    
094        public List<Long> getBuildingIds() {
095            if (isMultiRoom()) {
096                List<Long> ret = new ArrayList<Long>(iRoomLocations.size());
097                for (RoomLocation r : iRoomLocations) {
098                    ret.add(r.getBuildingId());
099                }
100                return ret;
101            } else {
102                List<Long> ret = new ArrayList<Long>(1);
103                ret.add(iRoomLocation.getBuildingId());
104                return ret;
105            }
106        }
107    
108        public List<Long> getRoomIds() {
109            if (isMultiRoom()) {
110                List<Long> ret = new ArrayList<Long>(iRoomLocations.size());
111                for (RoomLocation r : iRoomLocations) {
112                    ret.add(r.getId());
113                }
114                return ret;
115            } else {
116                List<Long> ret = new ArrayList<Long>(1);
117                ret.add(iRoomLocation.getId());
118                return ret;
119            }
120        }
121    
122        public List<String> getRoomNames() {
123            if (isMultiRoom()) {
124                List<String> ret = new ArrayList<String>(iRoomLocations.size());
125                for (RoomLocation r : iRoomLocations) {
126                    ret.add(r.getName());
127                }
128                return ret;
129            } else {
130                List<String> ret = new ArrayList<String>(1);
131                if (iRoomLocation != null)
132                    ret.add(iRoomLocation.getName());
133                return ret;
134            }
135        }
136    
137        public List<Integer> getRoomPrefs() {
138            if (isMultiRoom()) {
139                List<Integer> ret = new ArrayList<Integer>(iRoomLocations.size());
140                for (RoomLocation r : iRoomLocations) {
141                    ret.add(r.getPreference());
142                }
143                return ret;
144            } else {
145                List<Integer> ret = new ArrayList<Integer>(1);
146                if (iRoomLocation != null)
147                    ret.add(iRoomLocation.getPreference());
148                return ret;
149            }
150        }
151    
152        public boolean isMultiRoom() {
153            return (iRoomLocations != null && iRoomLocations.size() != 1);
154        }
155    
156        public RoomLocation getRoomLocation(Long roomId) {
157            if (isMultiRoom()) {
158                for (RoomLocation r : iRoomLocations) {
159                    if (r.getId().equals(roomId))
160                        return r;
161                }
162            } else if (iRoomLocation != null && iRoomLocation.getId().equals(roomId))
163                return iRoomLocation;
164            return null;
165        }
166    
167        public boolean hasRoomLocation(Long roomId) {
168            if (isMultiRoom()) {
169                for (RoomLocation r : iRoomLocations) {
170                    if (r.getId().equals(roomId))
171                        return true;
172                }
173                return false;
174            } else
175                return iRoomLocation != null && iRoomLocation.getId().equals(roomId);
176        }
177    
178        public String getRoomName(String delim) {
179            if (isMultiRoom()) {
180                StringBuffer sb = new StringBuffer();
181                for (RoomLocation r : iRoomLocations) {
182                    if (sb.length() > 0)
183                        sb.append(delim);
184                    sb.append(r.getName());
185                }
186                return sb.toString();
187            } else {
188                return (getRoomLocation() == null ? "" : getRoomLocation().getName());
189            }
190        }
191    
192        @Override
193        public String getName() {
194            Lecture lecture = variable();
195            return getTimeLocation().getName() + " " + getRoomName(", ")
196                    + (lecture != null && lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : "");
197        }
198    
199        public String getLongName() {
200            Lecture lecture = variable();
201            if (isMultiRoom()) {
202                StringBuffer sb = new StringBuffer();
203                for (RoomLocation r : iRoomLocations) {
204                    if (sb.length() > 0)
205                        sb.append(", ");
206                    sb.append(r.getName());
207                }
208                return getTimeLocation().getLongName() + " " + sb
209                        + (lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : "");
210            } else
211                return getTimeLocation().getLongName()
212                        + (getRoomLocation() == null ? "" : " " + getRoomLocation().getName())
213                        + (lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : "");
214        }
215    
216        public boolean sameRooms(Placement placement) {
217            if (placement.isMultiRoom() != isMultiRoom())
218                return false;
219            if (isMultiRoom()) {
220                if (placement.getRoomLocations().size() != getRoomLocations().size())
221                    return false;
222                return placement.getRoomLocations().containsAll(getRoomLocations());
223            } else {
224                if (placement.getRoomLocation() == null)
225                    return getRoomLocation() == null;
226                return placement.getRoomLocation().equals(getRoomLocation());
227            }
228        }
229    
230        public boolean shareRooms(Placement placement) {
231            if (isMultiRoom()) {
232                if (placement.isMultiRoom()) {
233                    for (RoomLocation rl : getRoomLocations()) {
234                        if (rl.getRoomConstraint() == null || !rl.getRoomConstraint().getConstraint())
235                            continue;
236                        if (placement.getRoomLocations().contains(rl))
237                            return true;
238                    }
239                    return false;
240                } else {
241                    return getRoomLocations().contains(placement.getRoomLocation());
242                }
243            } else {
244                if (getRoomLocation().getRoomConstraint() == null || !getRoomLocation().getRoomConstraint().getConstraint())
245                    return false;
246                if (placement.isMultiRoom()) {
247                    return placement.getRoomLocations().contains(getRoomLocation());
248                } else {
249                    return getRoomLocation().equals(placement.getRoomLocation());
250                }
251            }
252        }
253    
254        public int nrDifferentRooms(Placement placement) {
255            if (isMultiRoom()) {
256                int ret = 0;
257                for (RoomLocation r : getRoomLocations()) {
258                    if (!placement.getRoomLocations().contains(r))
259                        ret++;
260                }
261                return ret;
262            } else {
263                return (placement.getRoomLocation().equals(getRoomLocation()) ? 0 : 1);
264            }
265        }
266    
267        public int nrDifferentBuildings(Placement placement) {
268            if (isMultiRoom()) {
269                int ret = 0;
270                for (RoomLocation r : getRoomLocations()) {
271                    boolean contains = false;
272                    for (RoomLocation q : placement.getRoomLocations()) {
273                        if (ToolBox.equals(r.getBuildingId(), q.getBuildingId()))
274                            contains = true;
275                    }
276                    if (!contains)
277                        ret++;
278                }
279                return ret;
280            } else {
281                return (ToolBox.equals(placement.getRoomLocation().getBuildingId(), getRoomLocation().getBuildingId()) ? 0
282                        : 1);
283            }
284        }
285    
286        public int sumRoomPreference() {
287            if (isMultiRoom()) {
288                int ret = 0;
289                for (RoomLocation r : getRoomLocations()) {
290                    ret += r.getPreference();
291                }
292                return ret;
293            } else {
294                return getRoomLocation().getPreference();
295            }
296        }
297    
298        public int getRoomPreference() {
299            if (isMultiRoom()) {
300                PreferenceCombination p = PreferenceCombination.getDefault();
301                for (RoomLocation r : getRoomLocations()) {
302                    p.addPreferenceInt(r.getPreference());
303                }
304                return p.getPreferenceInt();
305            } else {
306                return getRoomLocation().getPreference();
307            }
308        }
309    
310        public int getRoomSize() {
311            if (isMultiRoom()) {
312                if (getRoomLocations().isEmpty()) return 0;
313                int roomSize = Integer.MAX_VALUE;
314                for (RoomLocation r : getRoomLocations()) {
315                    roomSize = Math.min(roomSize, r.getRoomSize());
316                }
317                return roomSize;
318            } else {
319                return getRoomLocation().getRoomSize();
320            }
321        }
322    
323        public boolean isHard() {
324            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(getTimeLocation().getPreference())))
325                return true;
326            if (getRoomLocation() != null && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(getRoomLocation().getPreference())))
327                return true;
328            Lecture lecture = variable();
329            for (GroupConstraint gc : lecture.hardGroupSoftConstraints()) {
330                if (gc.isSatisfied())
331                    continue;
332                if (Constants.sPreferenceProhibited.equals(gc.getPrologPreference()))
333                    return true;
334                if (Constants.sPreferenceRequired.equals(gc.getPrologPreference()))
335                    return true;
336            }
337            return false;
338        }
339    
340        public boolean sameTime(Placement placement) {
341            return placement.getTimeLocation().equals(getTimeLocation());
342        }
343    
344        @Override
345        public boolean equals(Object object) {
346            if (object == null || !(object instanceof Placement))
347                return false;
348            Placement placement = (Placement) object;
349            if (placement.getId() == getId())
350                return true; // quick check
351            Lecture lecture = placement.variable();
352            Lecture thisLecture = variable();
353            if (lecture != null && thisLecture != null && !lecture.getClassId().equals(thisLecture.getClassId()))
354                return false;
355            if (!sameRooms(placement))
356                return false;
357            if (!sameTime(placement))
358                return false;
359            return true;
360        }
361    
362        @Override
363        public int hashCode() {
364            return iHashCode;
365        }
366    
367        @Override
368        public String toString() {
369            return variable().getName() + " " + getName();
370        }
371    
372        /** Distance between two placements */
373        public static double getDistanceInMeters(DistanceMetric m, Placement p1, Placement p2) {
374            if (p1.isMultiRoom()) {
375                if (p2.isMultiRoom()) {
376                    double dist = 0.0;
377                    for (RoomLocation r1 : p1.getRoomLocations()) {
378                        for (RoomLocation r2 : p2.getRoomLocations()) {
379                            dist = Math.max(dist, r1.getDistanceInMeters(m, r2));
380                        }
381                    }
382                    return dist;
383                } else {
384                    if (p2.getRoomLocation() == null)
385                        return 0.0;
386                    double dist = 0.0;
387                    for (RoomLocation r1 : p1.getRoomLocations()) {
388                        dist = Math.max(dist, r1.getDistanceInMeters(m, p2.getRoomLocation()));
389                    }
390                    return dist;
391                }
392            } else if (p2.isMultiRoom()) {
393                if (p1.getRoomLocation() == null)
394                    return 0.0;
395                double dist = 0.0;
396                for (RoomLocation r2 : p2.getRoomLocations()) {
397                    dist = Math.max(dist, p1.getRoomLocation().getDistanceInMeters(m, r2));
398                }
399                return dist;
400            } else {
401                if (p1.getRoomLocation() == null || p2.getRoomLocation() == null)
402                    return 0.0;
403                return p1.getRoomLocation().getDistanceInMeters(m, p2.getRoomLocation());
404            }
405        }
406        
407        /** Distance between two placements */
408        public static int getDistanceInMinutes(DistanceMetric m, Placement p1, Placement p2) {
409            if (p1.isMultiRoom()) {
410                if (p2.isMultiRoom()) {
411                    int dist = 0;
412                    for (RoomLocation r1 : p1.getRoomLocations()) {
413                        for (RoomLocation r2 : p2.getRoomLocations()) {
414                            dist = Math.max(dist, r1.getDistanceInMinutes(m, r2));
415                        }
416                    }
417                    return dist;
418                } else {
419                    if (p2.getRoomLocation() == null)
420                        return 0;
421                    int dist = 0;
422                    for (RoomLocation r1 : p1.getRoomLocations()) {
423                        dist = Math.max(dist, r1.getDistanceInMinutes(m, p2.getRoomLocation()));
424                    }
425                    return dist;
426                }
427            } else if (p2.isMultiRoom()) {
428                if (p1.getRoomLocation() == null)
429                    return 0;
430                int dist = 0;
431                for (RoomLocation r2 : p2.getRoomLocations()) {
432                    dist = Math.max(dist, p1.getRoomLocation().getDistanceInMinutes(m, r2));
433                }
434                return dist;
435            } else {
436                if (p1.getRoomLocation() == null || p2.getRoomLocation() == null)
437                    return 0;
438                return p1.getRoomLocation().getDistanceInMinutes(m, p2.getRoomLocation());
439            }
440        }
441    
442        public int getCommitedConflicts() {
443            int ret = 0;
444            Lecture lecture = variable();
445            for (Student student : lecture.students()) {
446                ret += student.countConflictPlacements(this);
447            }
448            return ret;
449        }
450    
451        public Long getAssignmentId() {
452            return iAssignmentId;
453        }
454    
455        public void setAssignmentId(Long assignmentId) {
456            iAssignmentId = assignmentId;
457        }
458    
459        public boolean canShareRooms(Placement other) {
460            return (variable()).canShareRoom(other.variable());
461        }
462    
463        public boolean isValid() {
464            Lecture lecture = variable();
465            if (!lecture.isValid(this))
466                return false;
467            for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
468                if (!ic.isAvailable(lecture, this))
469                    return false;
470            }
471            if (lecture.getNrRooms() > 0) {
472                if (isMultiRoom()) {
473                    for (RoomLocation roomLocation : getRoomLocations()) {
474                        if (roomLocation.getRoomConstraint() != null
475                                && !roomLocation.getRoomConstraint().isAvailable(lecture, getTimeLocation(),
476                                        lecture.getScheduler()))
477                            return false;
478                    }
479                } else {
480                    if (getRoomLocation().getRoomConstraint() != null
481                            && !getRoomLocation().getRoomConstraint().isAvailable(lecture, getTimeLocation(),
482                                    lecture.getScheduler()))
483                        return false;
484                }
485            }
486            return true;
487        }
488    
489        public String getNotValidReason() {
490            Lecture lecture = variable();
491            String reason = lecture.getNotValidReason(this);
492            if (reason != null)
493                return reason;
494            for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
495                if (!ic.isAvailable(lecture, this)) {
496                    if (!ic.isAvailable(lecture, getTimeLocation()))
497                        return "instructor " + ic.getName() + " not available at " + getTimeLocation().getLongName();
498                    else
499                        return "placement " + getTimeLocation().getLongName() + " " + getRoomName(", ")
500                                + " is too far for instructor " + ic.getName();
501                }
502            }
503            if (lecture.getNrRooms() > 0) {
504                if (isMultiRoom()) {
505                    for (RoomLocation roomLocation : getRoomLocations()) {
506                        if (roomLocation.getRoomConstraint() != null
507                                && !roomLocation.getRoomConstraint().isAvailable(lecture, getTimeLocation(),
508                                        lecture.getScheduler()))
509                            return "room " + roomLocation.getName() + " not available at "
510                                    + getTimeLocation().getLongName();
511                    }
512                } else {
513                    if (getRoomLocation().getRoomConstraint() != null
514                            && !getRoomLocation().getRoomConstraint().isAvailable(lecture, getTimeLocation(),
515                                    lecture.getScheduler()))
516                        return "room " + getRoomLocation().getName() + " not available at "
517                                + getTimeLocation().getLongName();
518                }
519            }
520            return reason;
521        }
522    
523        public int getNrRooms() {
524            if (iRoomLocations != null)
525                return iRoomLocations.size();
526            return (iRoomLocation == null ? 0 : 1);
527        }
528    
529        public int getSpreadPenalty() {
530            int spread = 0;
531            for (SpreadConstraint sc : variable().getSpreadConstraints()) {
532                spread += sc.getPenalty(this);
533            }
534            return spread;
535        }
536    
537        public int getMaxSpreadPenalty() {
538            int spread = 0;
539            for (SpreadConstraint sc : variable().getSpreadConstraints()) {
540                spread += sc.getMaxPenalty(this);
541            }
542            return spread;
543        }
544    
545        @Override
546        public double toDouble() {
547            double ret = 0.0;
548            for (Criterion<Lecture, Placement> criterion: variable().getModel().getCriteria())
549                ret += criterion.getWeightedValue(this, null);
550            return ret;
551        }
552    
553        private transient Object iAssignment = null;
554    
555        public Object getAssignment() {
556            return iAssignment;
557        }
558    
559        public void setAssignment(Object assignment) {
560            iAssignment = assignment;
561        }
562    }