001package org.cpsolver.studentsct.model;
002
003import java.util.ArrayList;
004import java.util.HashSet;
005import java.util.List;
006import java.util.Set;
007
008import org.cpsolver.studentsct.reservation.Reservation;
009
010
011/**
012 * Representation of a scheduling subpart. Each scheduling subpart contains id,
013 * instructional type, name, instructional offering configuration, and a list of
014 * sections. Optionally, parent-child relation between subparts can be defined. <br>
015 * <br>
016 * 
017 * @version StudentSct 1.3 (Student Sectioning)<br>
018 *          Copyright (C) 2007 - 2014 Tomas Muller<br>
019 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
020 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
021 * <br>
022 *          This library is free software; you can redistribute it and/or modify
023 *          it under the terms of the GNU Lesser General Public License as
024 *          published by the Free Software Foundation; either version 3 of the
025 *          License, or (at your option) any later version. <br>
026 * <br>
027 *          This library is distributed in the hope that it will be useful, but
028 *          WITHOUT ANY WARRANTY; without even the implied warranty of
029 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
030 *          Lesser General Public License for more details. <br>
031 * <br>
032 *          You should have received a copy of the GNU Lesser General Public
033 *          License along with this library; if not see
034 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
035 */
036public class Subpart implements Comparable<Subpart> {
037    private long iId = -1;
038    private String iInstructionalType = null;
039    private String iName = null;
040    private List<Section> iSections = new ArrayList<Section>();
041    private Config iConfig = null;
042    private Subpart iParent = null;
043    private boolean iAllowOverlap = false;
044    private String iCredit = null;
045
046    /**
047     * Constructor
048     * 
049     * @param id
050     *            scheduling subpart unique id
051     * @param itype
052     *            instructional type
053     * @param name
054     *            subpart name
055     * @param config
056     *            instructional offering configuration to which this subpart
057     *            belongs
058     * @param parent
059     *            parent subpart, if parent-child relation is defined between
060     *            subparts
061     */
062    public Subpart(long id, String itype, String name, Config config, Subpart parent) {
063        iId = id;
064        iInstructionalType = itype;
065        iName = name;
066        iConfig = config;
067        iParent = parent;
068        iConfig.getSubparts().add(this);
069    }
070
071    /** Subpart id 
072     * @return scheduling subpart unique id
073     **/
074    public long getId() {
075        return iId;
076    }
077
078    /** Instructional type, e.g., Lecture, Recitation or Laboratory 
079     * @return instructional type
080     **/
081    public String getInstructionalType() {
082        return iInstructionalType;
083    }
084
085    /** Subpart name 
086     * @return scheduling subpart name
087     **/
088    public String getName() {
089        return iName;
090    }
091
092    /** Instructional offering configuration to which this subpart belongs 
093     * @return instructional offering configuration
094     **/
095    public Config getConfig() {
096        return iConfig;
097    }
098
099    /** List of sections 
100     * @return classes of this scheduling supart
101     **/
102    public List<Section> getSections() {
103        return iSections;
104    }
105
106    /** Parent subpart, if parent-child relation is defined between subparts 
107     * @return parent scheduling subpart
108     **/
109    public Subpart getParent() {
110        return iParent;
111    }
112
113    @Override
114    public String toString() {
115        return getName();
116    }
117
118    /**
119     * True, if this subpart is parent (or parent of a parent etc.) of the given
120     * subpart
121     * @param subpart parent scheduling subpart
122     * @return true if parent (even indirect)
123     */
124    public boolean isParentOf(Subpart subpart) {
125        if (subpart.getParent() == null)
126            return false;
127        if (subpart.getParent().equals(this))
128            return true;
129        return isParentOf(subpart.getParent());
130    }
131
132    /**
133     * Compare two subparts: put parents first, use ids if there is no
134     * parent-child relation
135     */
136    @Override
137    public int compareTo(Subpart s) {
138        if (isParentOf(s))
139            return -1;
140        if (s.isParentOf(this))
141            return 1;
142        int cmp = getInstructionalType().compareTo(s.getInstructionalType());
143        if (cmp != 0)
144            return cmp;
145        return Double.compare(getId(), s.getId());
146    }
147
148    /** List of available choices of the sections of this subpart. 
149     * @return set of available choices
150     **/
151    public Set<Choice> getChoices() {
152        Set<Choice> choices = new HashSet<Choice>();
153        for (Section section : getSections()) {
154            choices.add(section.getChoice());
155        }
156        return choices;
157    }
158
159    /** Minimal penalty from {@link Section#getPenalty()} 
160     * @return minimal penalty
161     **/
162    public double getMinPenalty() {
163        double min = Double.MAX_VALUE;
164        for (Section section : getSections()) {
165            min = Math.min(min, section.getPenalty());
166        }
167        return (min == Double.MAX_VALUE ? 0.0 : min);
168    }
169
170    /** Maximal penalty from {@link Section#getPenalty()} 
171     * @return maximal penalty
172     **/
173    public double getMaxPenalty() {
174        double max = Double.MIN_VALUE;
175        for (Section section : getSections()) {
176            max = Math.max(max, section.getPenalty());
177        }
178        return (max == Double.MIN_VALUE ? 0.0 : max);
179    }
180
181    /** Return children subparts 
182     * @return children scheduling subparts
183     **/
184    public List<Subpart> getChildren() {
185        List<Subpart> ret = new ArrayList<Subpart>(getConfig().getSubparts().size());
186        for (Subpart s : getConfig().getSubparts()) {
187            if (this.equals(s.getParent()))
188                ret.add(s);
189        }
190        return ret;
191    }
192    
193    /** Return true if overlaps are allowed, but the number of overlapping slots should be minimized. 
194     * @return true if overlaps of classes of this scheduling subpart are allowed
195     **/
196    public boolean isAllowOverlap() {
197        return iAllowOverlap;
198    }
199    
200    /** Set to true if overlaps are allowed, but the number of overlapping slots should be minimized (defaults to false). 
201     * @param allowOverlap are overlaps of classes of this scheduling subpart allowed
202     **/
203    public void setAllowOverlap(boolean allowOverlap) {
204        iAllowOverlap = allowOverlap;
205    }
206    
207    /**
208     * Get reservations that require sections of this subpart
209     * @return reservations that require a class of this scheduling subpart
210     */
211    public synchronized List<Reservation> getSectionReservations() {
212        if (iSectionReservations == null) {
213            iSectionReservations = new ArrayList<Reservation>();
214            for (Reservation r: getConfig().getOffering().getReservations()) {
215                if (r.getSections(this) != null)
216                    iSectionReservations.add(r);
217            }
218        }
219        return iSectionReservations;
220    }
221    private List<Reservation> iSectionReservations = null;
222    
223    /**
224     * Clear reservation information that was cached on this subpart or below
225     */
226    public synchronized void clearReservationCache() {
227        for (Section s: getSections())
228            s.clearReservationCache();
229        iSectionReservations = null;
230    }
231    
232    /**
233     * Sum of the section limits (unlimited, if one or more sections are unlimited)
234     * @return total class limit
235     */
236    public int getLimit() {
237        if (iLimit == null)
238            iLimit = getLimitNoCache();
239        return iLimit;
240    }
241    private int getLimitNoCache() {
242        int limit = 0;
243        for (Section section: getSections()) {
244            if (section.getLimit() < 0) return -1;
245            limit += section.getLimit();
246        }
247        return limit;
248    }
249    private Integer iLimit = null; 
250    
251    @Override
252    public boolean equals(Object o) {
253        if (o == null || !(o instanceof Subpart)) return false;
254        return getId() == ((Subpart)o).getId();
255    }
256    
257    @Override
258    public int hashCode() {
259        return (int) (iId ^ (iId >>> 32));
260    }
261    
262    /**
263     * Set credit (Online Student Scheduling only)
264     * @param credit scheduling subpart credit
265     */
266    public void setCredit(String credit) { iCredit = credit; }
267    
268    /**
269     * Get credit (Online Student Scheduling only)
270     * @return scheduling subpart credit
271     */
272    public String getCredit() { return iCredit; }
273}