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    
044        /**
045         * Constructor
046         * 
047         * @param id
048         *            scheduling subpart unique id
049         * @param itype
050         *            instructional type
051         * @param name
052         *            subpart name
053         * @param config
054         *            instructional offering configuration to which this subpart
055         *            belongs
056         * @param parent
057         *            parent subpart, if parent-child relation is defined between
058         *            subparts
059         */
060        public Subpart(long id, String itype, String name, Config config, Subpart parent) {
061            iId = id;
062            iInstructionalType = itype;
063            iName = name;
064            iConfig = config;
065            iParent = parent;
066            iConfig.getSubparts().add(this);
067        }
068    
069        /** Subpart id */
070        public long getId() {
071            return iId;
072        }
073    
074        /** Instructional type, e.g., Lecture, Recitation or Laboratory */
075        public String getInstructionalType() {
076            return iInstructionalType;
077        }
078    
079        /** Subpart name */
080        public String getName() {
081            return iName;
082        }
083    
084        /** Instructional offering configuration to which this subpart belongs */
085        public Config getConfig() {
086            return iConfig;
087        }
088    
089        /** List of sections */
090        public List<Section> getSections() {
091            return iSections;
092        }
093    
094        /** Parent subpart, if parent-child relation is defined between subparts */
095        public Subpart getParent() {
096            return iParent;
097        }
098    
099        @Override
100        public String toString() {
101            return getName();
102        }
103    
104        /**
105         * True, if this subpart is parent (or parent of a parent etc.) of the given
106         * subpart
107         */
108        public boolean isParentOf(Subpart subpart) {
109            if (subpart.getParent() == null)
110                return false;
111            if (subpart.getParent().equals(this))
112                return true;
113            return isParentOf(subpart.getParent());
114        }
115    
116        /**
117         * Compare two subparts: put parents first, use ids if there is no
118         * parent-child relation
119         */
120        @Override
121        public int compareTo(Subpart s) {
122            if (isParentOf(s))
123                return -1;
124            if (s.isParentOf(this))
125                return 1;
126            int cmp = getInstructionalType().compareTo(s.getInstructionalType());
127            if (cmp != 0)
128                return cmp;
129            return Double.compare(getId(), s.getId());
130        }
131    
132        /** List of available choices of the sections of this subpart. */
133        public Set<Choice> getChoices() {
134            Set<Choice> choices = new HashSet<Choice>();
135            for (Section section : getSections()) {
136                choices.add(section.getChoice());
137            }
138            return choices;
139        }
140    
141        /** Minimal penalty from {@link Section#getPenalty()} */
142        public double getMinPenalty() {
143            double min = Double.MAX_VALUE;
144            for (Section section : getSections()) {
145                min = Math.min(min, section.getPenalty());
146            }
147            return (min == Double.MAX_VALUE ? 0.0 : min);
148        }
149    
150        /** Maximal penalty from {@link Section#getPenalty()} */
151        public double getMaxPenalty() {
152            double max = Double.MIN_VALUE;
153            for (Section section : getSections()) {
154                max = Math.max(max, section.getPenalty());
155            }
156            return (max == Double.MIN_VALUE ? 0.0 : max);
157        }
158    
159        /** Return children subparts */
160        public List<Subpart> getChildren() {
161            List<Subpart> ret = new ArrayList<Subpart>(getConfig().getSubparts().size());
162            for (Subpart s : getConfig().getSubparts()) {
163                if (this.equals(s.getParent()))
164                    ret.add(s);
165            }
166            return ret;
167        }
168        
169        /** Return true if overlaps are allowed, but the number of overlapping slots should be minimized. */
170        public boolean isAllowOverlap() {
171            return iAllowOverlap;
172        }
173        
174        /** Set to true if overlaps are allowed, but the number of overlapping slots should be minimized (defaults to false). */
175        public void setAllowOverlap(boolean allowOverlap) {
176            iAllowOverlap = allowOverlap;
177        }
178        
179        /**
180         * Get reservations that require sections of this subpart
181         */
182        public List<Reservation> getSectionReservations() {
183            if (iSectionReservations == null) {
184                iSectionReservations = new ArrayList<Reservation>();
185                for (Reservation r: getConfig().getOffering().getReservations()) {
186                    if (r.getSections(this) != null)
187                        iSectionReservations.add(r);
188                }
189            }
190            return iSectionReservations;
191        }
192        private List<Reservation> iSectionReservations = null;
193        
194        /**
195         * Clear reservation information that was cached on this subpart or below
196         */
197        public void clearReservationCache() {
198            for (Section s: getSections())
199                s.clearReservationCache();
200            iSectionReservations = null;
201        }
202        
203        /**
204         * Sum of the section limits (unlimited, if one or more sections are unlimited)
205         */
206        public int getLimit() {
207            if (iLimit == null)
208                iLimit = getLimitNoCache();
209            return iLimit;
210        }
211        private int getLimitNoCache() {
212            int limit = 0;
213            for (Section section: getSections()) {
214                if (section.getLimit() < 0) return -1;
215                limit += section.getLimit();
216            }
217            return limit;
218        }
219        private Integer iLimit = null; 
220        
221        @Override
222        public boolean equals(Object o) {
223            if (o == null || !(o instanceof Subpart)) return false;
224            return getId() == ((Subpart)o).getId();
225        }
226        
227        @Override
228        public int hashCode() {
229            return (int) (iId ^ (iId >>> 32));
230        }
231    }