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