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