001package org.cpsolver.studentsct.reservation;
002
003import java.util.HashMap;
004import java.util.HashSet;
005import java.util.Map;
006import java.util.Set;
007
008import org.cpsolver.studentsct.model.Config;
009import org.cpsolver.studentsct.model.Course;
010import org.cpsolver.studentsct.model.CourseRequest;
011import org.cpsolver.studentsct.model.Enrollment;
012import org.cpsolver.studentsct.model.Offering;
013import org.cpsolver.studentsct.model.Request;
014import org.cpsolver.studentsct.model.Section;
015import org.cpsolver.studentsct.model.Student;
016import org.cpsolver.studentsct.model.Subpart;
017
018
019
020/**
021 * Abstract restriction. Restrictions are like reservations, that must be used
022 * and that do not reserve any space. Except, there can be more than one restriction
023 * on an offering and the student must meet at least one that applies to her/him. 
024 * 
025 * <br>
026 * <br>
027 * 
028 * @author  Tomáš Müller
029 * @version StudentSct 1.3 (Student Sectioning)<br>
030 *          Copyright (C) 2007 - 2020 Tomáš Müller<br>
031 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
032 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
033 * <br>
034 *          This library is free software; you can redistribute it and/or modify
035 *          it under the terms of the GNU Lesser General Public License as
036 *          published by the Free Software Foundation; either version 3 of the
037 *          License, or (at your option) any later version. <br>
038 * <br>
039 *          This library is distributed in the hope that it will be useful, but
040 *          WITHOUT ANY WARRANTY; without even the implied warranty of
041 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
042 *          Lesser General Public License for more details. <br>
043 * <br>
044 *          You should have received a copy of the GNU Lesser General Public
045 *          License along with this library; if not see
046 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
047 */
048public abstract class Restriction {
049    /** Restriction unique id */
050    private long iId = 0;
051    
052    /** Instructional offering on which the restriction is set, required */
053    private Offering iOffering;
054
055    /** One or more configurations, if applicable */ 
056    private Set<Config> iConfigs = new HashSet<Config>();
057    
058    /** One or more sections, if applicable */
059    private Map<Subpart, Set<Section>> iSections = new HashMap<Subpart, Set<Section>>();
060    
061    /**
062     * Constructor
063     * @param id restriction unique id
064     * @param offering instructional offering on which the restriction is set
065     */
066    public Restriction(long id, Offering offering) {
067        iId = id;
068        iOffering = offering;
069        iOffering.getRestrictions().add(this);
070        iOffering.clearRestrictionCache();
071    }
072    
073    /**
074     * Restriction  id
075     * @return restriction unique id
076     */
077    public long getId() { return iId; }
078    
079    /**
080     * Returns true if the student is applicable for the restriction
081     * @param student a student 
082     * @param course a course
083     * @return true if student can use the restriction to get into the course / configuration / section
084     */
085    public abstract boolean isApplicable(Student student, Course course);
086    
087    @Deprecated
088    public boolean isApplicable(Student student) {
089        if (isApplicable(student, null)) return true;
090        for (Request r: student.getRequests()) {
091            if (r instanceof CourseRequest) {
092                for (Course course: ((CourseRequest) r).getCourses()) {
093                    if (course.getOffering().equals(getOffering()) && isApplicable(student, course))
094                        return true;
095                }
096            }
097        }
098        return false;
099    }
100
101    /**
102     * Instructional offering on which the restriction is set.
103     * @return instructional offering
104     */
105    public Offering getOffering() { return iOffering; }
106    
107    /**
108     * One or more configurations on which the restriction is set.
109     * @return instructional offering configurations
110     */
111    public Set<Config> getConfigs() { return iConfigs; }
112    
113    /**
114     * Add a configuration (of the offering {@link Restriction#getOffering()}) to this restriction
115     * @param config instructional offering configuration
116     */
117    public void addConfig(Config config) {
118        iConfigs.add(config);
119    }
120    
121    /**
122     * One or more sections on which the restriction is set.
123     * @return class class restrictions
124     */
125    public Map<Subpart, Set<Section>> getSections() { return iSections; }
126    
127    /**
128     * One or more sections on which the restriction is set (optional).
129     * @param subpart scheduling subpart
130     * @return class restrictions for the given scheduling subpart
131     */
132    public Set<Section> getSections(Subpart subpart) {
133        return iSections.get(subpart);
134    }
135    
136    /**
137     * Add a section (of the offering {@link Restriction#getOffering()}) to this restriction.
138     * This will also add all parent sections and the appropriate configuration to the offering.
139     * @param section a class restriction
140     */
141    public void addSection(Section section) {
142        addConfig(section.getSubpart().getConfig());
143        while (section != null) {
144            Set<Section> sections = iSections.get(section.getSubpart());
145            if (sections == null) {
146                sections = new HashSet<Section>();
147                iSections.put(section.getSubpart(), sections);
148            }
149            sections.add(section);
150            section = section.getParent();
151        }
152    }
153    
154    /**
155     * Return true if the given enrollment meets the restriction.
156     * @param enrollment given enrollment
157     * @return true if the given enrollment meets the restriction
158     */
159    public boolean isIncluded(Enrollment enrollment) {
160        // Free time request are never included
161        if (enrollment.getConfig() == null) return false;
162        
163        // Check the offering
164        if (!iOffering.equals(enrollment.getConfig().getOffering())) return false;
165        
166        // no restrictions -> not included
167        if (iConfigs.isEmpty() && iSections.isEmpty()) return false;
168        
169        // If there are configurations, check the configuration
170        if (!iConfigs.isEmpty() && !iConfigs.contains(enrollment.getConfig())) return false;
171        
172        // Check all the sections of the enrollment
173        for (Section section: enrollment.getSections()) {
174            Set<Section> sections = iSections.get(section.getSubpart());
175            if (sections != null && !sections.contains(section))
176                return false;
177        }
178        return true;
179    }
180    
181    public boolean isIncluded(Config config) {
182        // Check the offering
183        if (!iOffering.equals(config.getOffering())) return false;
184        
185        // no restrictions -> not included
186        if (iConfigs.isEmpty() && iSections.isEmpty()) return false;
187        
188        // if there are configurations, check the configuration
189        if (!iConfigs.isEmpty() && !iConfigs.contains(config)) return false;
190        return true;
191    }
192    
193    public boolean isIncluded(Section section) {
194        // Check the offering
195        if (!iOffering.equals(section.getSubpart().getConfig().getOffering())) return false;
196        
197        // no restrictions -> not included
198        if (iConfigs.isEmpty() && iSections.isEmpty()) return false;
199        
200        // if there are configurations, check the configuration
201        if (!iConfigs.isEmpty() && !iConfigs.contains(section.getSubpart().getConfig())) return false;
202        
203        // check the given section
204        Set<Section> sections = iSections.get(section.getSubpart());
205        if (sections != null && !sections.contains(section))
206            return false;
207        
208        return true;
209    }
210}