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     * Return true if the given choice has the same instructional type and time
130     * return true if the two choices have the same ime
131     */
132    public boolean sameTime(Choice choice) {
133        return getInstructionalType().equals(choice.getInstructionalType()) &&
134                (getTime() == null ? choice.getTime() == null : getTime().equals(choice.getTime()));
135    }
136
137    /**
138     * Instructor(s) id of the choice, can be null if the section has no
139     * instructor assigned
140     * @return selected instructors
141     */
142    public String getInstructorIds() {
143        return iInstructorIds;
144    }
145
146    /**
147     * Instructor(s) name of the choice, can be null if the section has no
148     * instructor assigned
149     * @return selected instructors
150     */
151    public String getInstructorNames() {
152        return iInstructorNames;
153    }
154    
155    /**
156     * Set instructor(s) id and name of the choice, can be null if the section has no
157     * instructor assigned
158     * @param instructorIds selected instructor ids
159     * @param instructorNames selected instructor names
160     */
161    public void setInstructor(String instructorIds, String instructorNames) {
162        iInstructorIds = instructorIds;
163        iInstructorNames = instructorNames;
164    }
165
166    /**
167     * Choice id combined from instructionalType, time and instructorIds in the
168     * following format: instructionalType|time|instructorIds where time is of
169     * format dayCode:startSlot:length:datePatternId
170     * @return choice id
171     */
172    public String getId() {
173        String ret = getInstructionalType() + "|";
174        if (getTime() != null)
175            ret += getTime().getDayCode() + ":" + getTime().getStartSlot() + ":" + getTime().getLength()
176                    + (getTime().getDatePatternId() == null ? "" : ":" + getTime().getDatePatternId());
177        if (getInstructorIds() != null)
178            ret += "|" + getInstructorIds();
179        return ret;
180    }
181
182    /** Compare two choices, based on {@link Choice#getId()} */
183    @Override
184    public boolean equals(Object o) {
185        if (o == null || !(o instanceof Choice))
186            return false;
187        return ((Choice) o).getId().equals(getId());
188    }
189
190    /** Choice hash id, based on {@link Choice#getId()} */
191    @Override
192    public int hashCode() {
193        return iHashCode;
194    }
195
196    /**
197     * List of sections of the instructional offering which represent this
198     * choice. Note that there can be multiple sections with the same choice
199     * (e.g., only if the room location differs).
200     * @return set of sections for matching this choice
201     */
202    public Set<Section> getSections() {
203        Set<Section> sections = new HashSet<Section>();
204        for (Config config : getOffering().getConfigs()) {
205            for (Subpart subpart : config.getSubparts()) {
206                if (!subpart.getInstructionalType().equals(getInstructionalType()))
207                    continue;
208                for (Section section : subpart.getSections()) {
209                    if (section.getChoice().equals(this))
210                        sections.add(section);
211                }
212            }
213        }
214        return sections;
215    }
216
217    /**
218     * List of parent sections of sections of the instructional offering which
219     * represent this choice. Note that there can be multiple sections with the
220     * same choice (e.g., only if the room location differs).
221     * @return set of parent sections
222     */
223    public Set<Section> getParentSections() {
224        Set<Section> parentSections = new HashSet<Section>();
225        for (Config config : getOffering().getConfigs()) {
226            for (Subpart subpart : config.getSubparts()) {
227                if (!subpart.getInstructionalType().equals(getInstructionalType()))
228                    continue;
229                if (subpart.getParent() == null)
230                    continue;
231                for (Section section : subpart.getSections()) {
232                    if (section.getChoice().equals(this) && section.getParent() != null)
233                        parentSections.add(section.getParent());
234                }
235            }
236        }
237        return parentSections;
238    }
239
240    /**
241     * Choice name: name of the appropriate subpart + long name of time +
242     * instructor(s) name
243     * @return choice name
244     */
245    public String getName() {
246        return (getOffering().getSubparts(getInstructionalType()).iterator().next()).getName()
247                + " "
248                + (getTime() == null ? "" : getTime().getLongName(true))
249                + (getInstructorIds() == null ? "" : getInstructorNames() != null ? " " + getInstructorNames() : " "
250                        + getInstructorIds());
251    }
252
253    @Override
254    public String toString() {
255        return getName();
256    }
257}