001    package net.sf.cpsolver.studentsct.model;
002    
003    import java.util.ArrayList;
004    import java.util.HashSet;
005    import java.util.List;
006    import java.util.Set;
007    
008    import 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 Tomas Muller<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     */
035    public 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    }