001package org.cpsolver.instructor.model;
002
003/**
004 * Attributes of an instructor. Each instructor can have a number of attributes and there are attribute preferences on teaching requests.
005 * Each attribute has an id, a name and a {@link Type}.
006 * 
007 * @author  Tomáš Müller
008 * @version IFS 1.3 (Instructor Sectioning)<br>
009 *          Copyright (C) 2016 Tomáš Müller<br>
010 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
011 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
012 * <br>
013 *          This library is free software; you can redistribute it and/or modify
014 *          it under the terms of the GNU Lesser General Public License as
015 *          published by the Free Software Foundation; either version 3 of the
016 *          License, or (at your option) any later version. <br>
017 * <br>
018 *          This library is distributed in the hope that it will be useful, but
019 *          WITHOUT ANY WARRANTY; without even the implied warranty of
020 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
021 *          Lesser General Public License for more details. <br>
022 * <br>
023 *          You should have received a copy of the GNU Lesser General Public
024 *          License along with this library; if not see
025 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
026 */
027public class Attribute {
028    private Long iAttributeId;
029    private String iAttributeName;
030    private Type iType;
031    private Attribute iParentAttribute;
032    
033    /**
034     * Constructor
035     * @param attributeId attribute id
036     * @param attributeName attribute name
037     * @param type attribute type
038     */
039    public Attribute(long attributeId, String attributeName, Type type) {
040        iAttributeId = attributeId;
041        iAttributeName = attributeName;
042        iType = type;
043    }
044    
045    /**
046     * Attribute id that was provided in the constructor
047     * @return attribute id
048     */
049    public Long getAttributeId() { return iAttributeId; }
050    
051    /**
052     * Attribute name that was provided in the constructor
053     * @return attribute name
054     */
055    public String getAttributeName() { return iAttributeName == null ? "A" + iAttributeId : iAttributeName; }
056    
057    /**
058     * Attribute type that was provided in the constructor
059     * @return attribute type
060     */
061    public Type getType() { return iType; }
062    
063    /**
064     * Parent attribute
065     */
066    public Attribute getParentAttribute() { return iParentAttribute; }
067    
068    /**
069     * Parent attribute
070     */
071    public void setParentAttribute(Attribute parent) { iParentAttribute = parent; }
072    
073    @Override
074    public int hashCode() {
075        return (getAttributeId() == null ? getAttributeName().hashCode() : getAttributeId().hashCode());
076    }
077    
078    @Override
079    public boolean equals(Object o) {
080        if (o == null || !(o instanceof Attribute)) return false;
081        Attribute a = (Attribute)o;
082        return getAttributeId() == null ? getAttributeName().equals(a.getAttributeName()) : getAttributeId().equals(a.getAttributeId());
083    }
084    
085    @Override
086    public String toString() { return getAttributeName() + " (" + getType() + ")"; }
087    
088    /**
089     * Attribute type. Each type has an id and a name. It can also define whether attributes of this type are required and/or conjunctive.
090     * If an attribute is required, this means that only instructors that have the attribute can be used, even if it is only preferred. This
091     * allows to put different preferences on multiple attribute that are required.
092     * If a teaching requests require two attributes that are of the same type which is conjunctive, only instructors that have BOTH attributes can be used.
093     * It the type is disjunctive (not conjunctive), it is sufficient for the instructor to have one of the two required attributes. 
094     */
095    public static class Type {
096        private Long iTypeId;
097        private String iTypeName;
098        private boolean iRequired;
099        private boolean iConjunctive;
100
101        /**
102         * Constructor
103         * @param typeId attribute type id
104         * @param typeName attribute type name
105         * @param conjunctive is attribute type conjunctive (if two attributes are required a student must have both). 
106         * @param required is the attribute type required
107         */
108        public Type(long typeId, String typeName, boolean conjunctive, boolean required) {
109            iTypeId = typeId;
110            iTypeName = typeName;
111            iConjunctive = conjunctive;
112            iRequired = required;
113        }
114        
115        /**
116         * Attribute type id that was provided in the constructor
117         * @return attribute type id
118         */
119        public Long getTypeId() { return iTypeId; }
120        
121        /**
122         * Attribute type name that was provided in the constructor
123         * @return attribute type name
124         */
125        public String getTypeName() { return iTypeName == null ? "T" + iTypeId : iTypeName; }
126        
127        /**
128         * If an attribute is required, this means that only instructors that have the attribute can be used, even if it is only preferred. This
129         * allows to put different preferences on multiple attribute that are required.
130         * @return true if this attribute type is required
131         */
132        public boolean isRequired() { return iRequired; }
133        
134        /**
135         * If a teaching requests require two attributes that are of the same type which is conjunctive, only instructors that have BOTH attributes can be used.
136         * It the type is disjunctive (not conjunctive), it is sufficient for the instructor to have one of the two required attributes.
137         * @return true if this attribute type is conjunctive, false if disjunctive
138         */
139        public boolean isConjunctive() { return iConjunctive; }
140
141        @Override
142        public int hashCode() {
143            return getTypeId() == null ? getTypeName().hashCode() : getTypeId().hashCode();
144        }
145        
146        @Override
147        public boolean equals(Object o) {
148            if (o == null || !(o instanceof Type)) return false;
149            Type t = (Type)o;
150            return getTypeId() == null ? getTypeName().equals(t.getTypeName()) : getTypeId().equals(t.getTypeId());
151        }
152        
153        @Override
154        public String toString() { return getTypeName(); }
155    }
156
157}