    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}