001package net.sf.cpsolver.studentsct.model;
002
003import java.util.BitSet;
004import java.util.HashSet;
005import java.util.Set;
006import java.util.StringTokenizer;
007
008import net.sf.cpsolver.coursett.model.TimeLocation;
009
010/**
011 * Student choice. Students have a choice of availabe time (but not room) and
012 * instructor(s).
013 * 
014 * Choices of subparts that have the same instrutional type are also merged
015 * together. For instance, a student have a choice of a time/instructor of a
016 * Lecture and of a Recitation.
017 * 
018 * <br>
019 * <br>
020 * 
021 * @version StudentSct 1.2 (Student Sectioning)<br>
022 *          Copyright (C) 2007 - 2010 Tomáš Müller<br>
023 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
024 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
025 * <br>
026 *          This library is free software; you can redistribute it and/or modify
027 *          it under the terms of the GNU Lesser General Public License as
028 *          published by the Free Software Foundation; either version 3 of the
029 *          License, or (at your option) any later version. <br>
030 * <br>
031 *          This library is distributed in the hope that it will be useful, but
032 *          WITHOUT ANY WARRANTY; without even the implied warranty of
033 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
034 *          Lesser General Public License for more details. <br>
035 * <br>
036 *          You should have received a copy of the GNU Lesser General Public
037 *          License along with this library; if not see
038 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
039 */
040public class Choice {
041    private Offering iOffering = null;
042    private String iInstructionalType = null;
043    private TimeLocation iTime = null;
044    private String iInstructorIds = null;
045    private String iInstructorNames = null;
046    private int iHashCode;
047
048    /**
049     * Constructor
050     * 
051     * @param offering
052     *            instructional offering to which the choice belongs
053     * @param instructionalType
054     *            instructional type to which the choice belongs (e.g., Lecture,
055     *            Recitation or Laboratory)
056     * @param time
057     *            time assignment
058     * @param instructorIds
059     *            instructor(s) id
060     * @param instructorNames
061     *            instructor(s) name
062     */
063    public Choice(Offering offering, String instructionalType, TimeLocation time, String instructorIds,
064            String instructorNames) {
065        iOffering = offering;
066        iInstructionalType = instructionalType;
067        iTime = time;
068        iInstructorIds = instructorIds;
069        iInstructorNames = instructorNames;
070        iHashCode = getId().hashCode();
071    }
072
073    /**
074     * Constructor
075     * 
076     * @param offering
077     *            instructional offering to which the choice belongs
078     * @param choiceId
079     *            choice id is in format instructionalType|time|instructorIds
080     *            where time is of format dayCode:startSlot:length:datePatternId
081     */
082    public Choice(Offering offering, String choiceId) {
083        iOffering = offering;
084        iInstructionalType = choiceId.substring(0, choiceId.indexOf('|'));
085        choiceId = choiceId.substring(choiceId.indexOf('|') + 1);
086        String timeId = null;
087        if (choiceId.indexOf('|') < 0) {
088            timeId = choiceId;
089        } else {
090            timeId = choiceId.substring(0, choiceId.indexOf('|'));
091            iInstructorIds = choiceId.substring(choiceId.indexOf('|') + 1);
092        }
093        if (timeId != null && timeId.length() > 0) {
094            StringTokenizer s = new StringTokenizer(timeId, ":");
095            int dayCode = Integer.parseInt(s.nextToken());
096            int startSlot = Integer.parseInt(s.nextToken());
097            int length = Integer.parseInt(s.nextToken());
098            Long datePatternId = (s.hasMoreElements() ? Long.valueOf(s.nextToken()) : null);
099            iTime = new TimeLocation(dayCode, startSlot, length, 0, 0, datePatternId, "N/A", new BitSet(), 0);
100        }
101        iHashCode = getId().hashCode();
102    }
103
104    /** Instructional offering to which this choice belongs */
105    public Offering getOffering() {
106        return iOffering;
107    }
108
109    /**
110     * Instructional type (e.g., Lecture, Recitation or Laboratory) to which
111     * this choice belongs
112     */
113    public String getInstructionalType() {
114        return iInstructionalType;
115    }
116
117    /** Time location of the choice */
118    public TimeLocation getTime() {
119        return iTime;
120    }
121
122    /**
123     * Instructor(s) id of the choice, can be null if the section has no
124     * instructor assigned
125     */
126    public String getInstructorIds() {
127        return iInstructorIds;
128    }
129
130    /**
131     * Instructor(s) name of the choice, can be null if the section has no
132     * instructor assigned
133     */
134    public String getInstructorNames() {
135        return iInstructorNames;
136    }
137    
138    /**
139     * Set instructor(s) id and name of the choice, can be null if the section has no
140     * instructor assigned
141     */
142    public void setInstructor(String instructorIds, String instructorNames) {
143        iInstructorIds = instructorIds;
144        iInstructorNames = instructorNames;
145    }
146
147    /**
148     * Choice id combined from instructionalType, time and instructorIds in the
149     * following format: instructionalType|time|instructorIds where time is of
150     * format dayCode:startSlot:length:datePatternId
151     */
152    public String getId() {
153        String ret = getInstructionalType() + "|";
154        if (getTime() != null)
155            ret += getTime().getDayCode() + ":" + getTime().getStartSlot() + ":" + getTime().getLength()
156                    + (getTime().getDatePatternId() == null ? "" : ":" + getTime().getDatePatternId());
157        if (getInstructorIds() != null)
158            ret += "|" + getInstructorIds();
159        return ret;
160    }
161
162    /** Compare two choices, based on {@link Choice#getId()} */
163    @Override
164    public boolean equals(Object o) {
165        if (o == null || !(o instanceof Choice))
166            return false;
167        return ((Choice) o).getId().equals(getId());
168    }
169
170    /** Choice hash id, based on {@link Choice#getId()} */
171    @Override
172    public int hashCode() {
173        return iHashCode;
174    }
175
176    /**
177     * List of sections of the instructional offering which represent this
178     * choice. Note that there can be multiple sections with the same choice
179     * (e.g., only if the room location differs).
180     */
181    public Set<Section> getSections() {
182        Set<Section> sections = new HashSet<Section>();
183        for (Config config : getOffering().getConfigs()) {
184            for (Subpart subpart : config.getSubparts()) {
185                if (!subpart.getInstructionalType().equals(getInstructionalType()))
186                    continue;
187                for (Section section : subpart.getSections()) {
188                    if (section.getChoice().equals(this))
189                        sections.add(section);
190                }
191            }
192        }
193        return sections;
194    }
195
196    /**
197     * List of parent sections of sections of the instructional offering which
198     * represent this choice. Note that there can be multiple sections with the
199     * same choice (e.g., only if the room location differs).
200     */
201    public Set<Section> getParentSections() {
202        Set<Section> parentSections = new HashSet<Section>();
203        for (Config config : getOffering().getConfigs()) {
204            for (Subpart subpart : config.getSubparts()) {
205                if (!subpart.getInstructionalType().equals(getInstructionalType()))
206                    continue;
207                if (subpart.getParent() == null)
208                    continue;
209                for (Section section : subpart.getSections()) {
210                    if (section.getChoice().equals(this) && section.getParent() != null)
211                        parentSections.add(section.getParent());
212                }
213            }
214        }
215        return parentSections;
216    }
217
218    /**
219     * Choice name: name of the appropriate subpart + long name of time +
220     * instructor(s) name
221     */
222    public String getName() {
223        return (getOffering().getSubparts(getInstructionalType()).iterator().next()).getName()
224                + " "
225                + (getTime() == null ? "" : getTime().getLongName())
226                + (getInstructorIds() == null ? "" : getInstructorNames() != null ? " " + getInstructorNames() : " "
227                        + getInstructorIds());
228    }
229
230    @Override
231    public String toString() {
232        return getName();
233    }
234}