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