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