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 }