001package org.cpsolver.studentsct.reservation;
002
003import java.util.Collection;
004import java.util.HashMap;
005import java.util.HashSet;
006import java.util.Map;
007import java.util.Set;
008
009import org.cpsolver.studentsct.model.AreaClassificationMajor;
010import org.cpsolver.studentsct.model.Course;
011import org.cpsolver.studentsct.model.Offering;
012import org.cpsolver.studentsct.model.Student;
013
014
015/**
016 * Curriculum reservation. Students are matched based on their academic area.
017 * If classifications and/or majors are included, student must match on them as well.  
018 * 
019 * <br>
020 * <br>
021 * 
022 * @author  Tomáš Müller
023 * @version StudentSct 1.3 (Student Sectioning)<br>
024 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
025 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
026 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
027 * <br>
028 *          This library is free software; you can redistribute it and/or modify
029 *          it under the terms of the GNU Lesser General Public License as
030 *          published by the Free Software Foundation; either version 3 of the
031 *          License, or (at your option) any later version. <br>
032 * <br>
033 *          This library is distributed in the hope that it will be useful, but
034 *          WITHOUT ANY WARRANTY; without even the implied warranty of
035 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
036 *          Lesser General Public License for more details. <br>
037 * <br>
038 *          You should have received a copy of the GNU Lesser General Public
039 *          License along with this library; if not see
040 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
041 */
042public class CurriculumReservation extends Reservation {
043    private double iLimit;
044    private Set<String> iAcadAreas = new HashSet<String>();
045    private Set<String> iClassifications = new HashSet<String>();
046    private Set<String> iMajors = new HashSet<String>();
047    private Set<String> iMinors = new HashSet<String>();
048    private Map<String, Set<String>> iConcentrations = null;
049    
050    /**
051     * Reservation priority (lower than individual and group reservations)
052     */
053    public static final int DEFAULT_PRIORITY = 500;
054    /**
055     * Curriculum reservation does not need to be used
056     */
057    public static final boolean DEFAULT_MUST_BE_USED = false;
058    /**
059     * Curriculum reservations can not assign over the limit.
060     */
061    public static final boolean DEFAULT_CAN_ASSIGN_OVER_LIMIT = false;
062    /**
063     * Overlaps are not allowed for curriculum reservations. 
064     */
065    public static final boolean DEFAULT_ALLOW_OVERLAP = false;
066    
067    /**
068     * Constructor
069     * @param id unique id
070     * @param limit reservation limit (-1 for unlimited)
071     * @param offering instructional offering on which the reservation is set
072     * @param acadAreas one or more academic areas
073     * @param classifications zero or more classifications (classifications must match if not empty)
074     * @param majors zero or more majors (majors must match if not empty)
075     * @param minors zero or more majors (minors must match if not empty)
076     */
077    public CurriculumReservation(long id, double limit, Offering offering, Collection<String> acadAreas, Collection<String> classifications, Collection<String> majors, Collection<String> minors) {
078        super(id, offering, DEFAULT_PRIORITY, DEFAULT_MUST_BE_USED, DEFAULT_CAN_ASSIGN_OVER_LIMIT, DEFAULT_ALLOW_OVERLAP);
079        iLimit = limit;
080        if (acadAreas != null)
081            iAcadAreas.addAll(acadAreas);
082        if (classifications != null)
083            iClassifications.addAll(classifications);
084        if (majors != null)
085            iMajors.addAll(majors);
086        if (minors != null)
087            iMinors.addAll(minors);
088    }
089    
090    /**
091     * Constructor
092     * @param id unique id
093     * @param limit reservation limit (-1 for unlimited)
094     * @param offering instructional offering on which the reservation is set
095     * @param acadArea academic area
096     * @param classifications zero or more classifications (classifications must match if not empty)
097     * @param majors zero or more majors (majors must match if not empty)
098     */
099    @Deprecated
100    public CurriculumReservation(long id, double limit, Offering offering, String acadArea, Collection<String> classifications, Collection<String> majors) {
101        super(id, offering, DEFAULT_PRIORITY, DEFAULT_MUST_BE_USED, DEFAULT_CAN_ASSIGN_OVER_LIMIT, DEFAULT_ALLOW_OVERLAP);
102        iLimit = limit;
103        iAcadAreas.add(acadArea);
104        if (classifications != null)
105            iClassifications.addAll(classifications);
106        if (majors != null)
107            iMajors.addAll(majors);
108    }
109    
110    /**
111     * Constructor
112     * @param id unique id
113     * @param limit reservation limit (-1 for unlimited)
114     * @param offering instructional offering on which the reservation is set
115     * @param acadAreas one or more academic areas
116     * @param classifications zero or more classifications (classifications must match if not empty)
117     * @param majors zero or more majors (majors must match if not empty)
118     * @param minors zero or more majors (minors must match if not empty)
119     * @param priority reservation priority
120     * @param mustBeUsed must this reservation be used
121     * @param canAssignOverLimit can assign over class / configuration / course limit
122     * @param allowOverlap does this reservation allow for overlaps
123     */
124    protected CurriculumReservation(long id, double limit, Offering offering, Collection<String> acadAreas, Collection<String> classifications, Collection<String> majors, Collection<String> minors,
125            int priority, boolean mustBeUsed, boolean canAssignOverLimit, boolean allowOverlap) {
126        super(id, offering, priority, mustBeUsed, canAssignOverLimit, allowOverlap);
127        iLimit = limit;
128        if (acadAreas != null)
129            iAcadAreas.addAll(acadAreas);
130        if (classifications != null)
131            iClassifications.addAll(classifications);
132        if (majors != null)
133            iMajors.addAll(majors);
134        if (minors != null)
135            iMinors.addAll(minors);
136    }
137    
138    /**
139     * Constructor
140     * @param id unique id
141     * @param limit reservation limit (-1 for unlimited)
142     * @param offering instructional offering on which the reservation is set
143     * @param acadArea academic area
144     * @param classifications zero or more classifications (classifications must match if not empty)
145     * @param majors zero or more majors (majors must match if not empty)
146     * @param priority reservation priority
147     * @param mustBeUsed must this reservation be used
148     * @param canAssignOverLimit can assign over class / configuration / course limit
149     * @param allowOverlap does this reservation allow for overlaps
150     */
151    @Deprecated
152    protected CurriculumReservation(long id, double limit, Offering offering, String acadArea, Collection<String> classifications, Collection<String> majors,
153            int priority, boolean mustBeUsed, boolean canAssignOverLimit, boolean allowOverlap) {
154        super(id, offering, priority, mustBeUsed, canAssignOverLimit, allowOverlap);
155        iLimit = limit;
156        iAcadAreas.add(acadArea);
157        if (classifications != null)
158            iClassifications.addAll(classifications);
159        if (majors != null)
160            iMajors.addAll(majors);
161    }
162
163    /**
164     * Reservation limit (-1 for unlimited)
165     */
166    @Override
167    public double getReservationLimit() {
168        return iLimit;
169    }
170
171    /**
172     * Set reservation limit (-1 for unlimited)
173     * @param limit reservation limit, -1 for unlimited
174     */
175    public void setReservationLimit(double limit) {
176        iLimit = limit;
177    }
178
179    
180    /**
181     * Academic areas
182     * @return selected academic areas
183     */
184    public Set<String> getAcademicAreas() {
185        return iAcadAreas;
186    }
187    
188    /**
189     * Academic area
190     * @return selected academic area
191     */
192    @Deprecated
193    public String getAcademicArea() {
194        if (getAcademicAreas().isEmpty()) return "";
195        return getAcademicAreas().iterator().next();
196    }
197    
198    /**
199     * Majors
200     * @return selected majors
201     */
202    public Set<String> getMajors() {
203        return iMajors;
204    }
205    
206    /**
207     * Minors
208     * @return selected minors
209     */
210    public Set<String> getMinors() {
211        return iMinors;
212    }
213    
214    /**
215     * Academic classifications
216     * @return selected academic classifications
217     */
218    public Set<String> getClassifications() {
219        return iClassifications;
220    }
221    
222    /** Concentrations for major */
223    public Set<String> getConcentrations(String major) {
224        return (iConcentrations == null ? null : iConcentrations.get(major));
225    }
226    
227    /** Add concentration for major */
228    public void addConcentration(String major, String concentration) {
229        if (iConcentrations == null) iConcentrations = new HashMap<String, Set<String>>();
230        Set<String> concentrations = iConcentrations.get(major);
231        if (concentrations == null) {
232            concentrations = new HashSet<String>();
233            iConcentrations.put(major, concentrations);
234        }
235        concentrations.add(concentration);
236    }
237
238    /**
239     * Check the area, classifications and majors
240     */
241    @Override
242    public boolean isApplicable(Student student, Course course) {
243        if (!getMajors().isEmpty() || getMinors().isEmpty())
244            for (AreaClassificationMajor acm: student.getAreaClassificationMajors())
245                if (getAcademicAreas().contains(acm.getArea()) &&
246                    (getClassifications().isEmpty() || getClassifications().contains(acm.getClassification())) &&
247                    (getMajors().isEmpty() || getMajors().contains(acm.getMajor()))) {
248                    Set<String> conc = getConcentrations(acm.getMajor());
249                    if (conc != null && !conc.isEmpty()) {
250                        return acm.getConcentration() != null && conc.contains(acm.getConcentration());
251                    } else {
252                        return true;
253                    }
254                }
255        if (!getMinors().isEmpty())
256            for (AreaClassificationMajor acm: student.getAreaClassificationMinors())
257                if (getAcademicAreas().contains(acm.getArea()) &&
258                    (getClassifications().isEmpty() || getClassifications().contains(acm.getClassification())) &&
259                    (getMinors().contains(acm.getMajor())))
260                        return true;
261        return false;
262    }
263    
264
265}