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