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