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 }