001package org.clafer.ast;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import org.clafer.common.Check;
007
008/**
009 * A Clafer in the model. A Clafer represents both the set and the type. A
010 * concrete is either abstract, concrete, or primitive.
011 *
012 * @author jimmy
013 */
014public abstract class AstClafer implements AstVar {
015
016    private final String name;
017    // The topmost Clafer in the type hierarchy.
018    protected final AstAbstractClafer claferClafer;
019    private AstAbstractClafer superClafer;
020    private AstRef ref;
021    private Card groupCard = new Card();
022    private final List<AstConcreteClafer> children = new ArrayList<>();
023    private final List<AstConstraint> constraints = new ArrayList<>();
024
025    AstClafer(String name, AstAbstractClafer claferClafer) {
026        this.name = Check.notNull(name);
027        this.claferClafer = claferClafer;
028    }
029
030    /**
031     * Returns the name of the Clafer.
032     *
033     * @return the name of the Clafer
034     */
035    @Override
036    public String getName() {
037        return name;
038    }
039
040    /**
041     * Check if this Clafer has a supertype. Every Clafer has a supertype except
042     * for the special Clafer at the root of the type hierarchy.
043     *
044     * @return {@code true} if and only if this Clafer has a supertype,
045     * {@code false} otherwise
046     */
047    public boolean hasSuperClafer() {
048        return superClafer != null;
049    }
050
051    /**
052     * Returns the supertype of this Clafer.
053     *
054     * @return the supertype of this Clafer
055     */
056    public AstAbstractClafer getSuperClafer() {
057        return superClafer;
058    }
059
060    /**
061     * Set the supertype of this Clafer.
062     *
063     * @param superClafer the supertype
064     * @return this Clafer
065     */
066    public AstClafer extending(AstAbstractClafer superClafer) {
067        Check.notNull(superClafer);
068        if (hasSuperClafer()) {
069            // Allowed to specialize.
070            if (!AstUtil.getSupers(superClafer).contains(getSuperClafer())) {
071                throw new IllegalArgumentException(this + " already has a super clafer");
072            }
073            getSuperClafer().removeSub(this);
074        }
075        superClafer.addSub(this);
076        this.superClafer = superClafer;
077        return this;
078    }
079
080    /**
081     * Check if this Clafer references another Clafer.
082     *
083     * @return {@code true} if and only if this Clafer references another
084     * Clafer, {@code false} otherwise
085     */
086    public boolean hasRef() {
087        return ref != null;
088    }
089
090    /**
091     * Returns this Clafer's reference
092     *
093     * @return this Clafer's reference
094     */
095    public AstRef getRef() {
096        return ref;
097    }
098
099    /**
100     * Set this Clafer's reference to target type.
101     *
102     * @param targetType the type to refer to
103     * @return this Clafer
104     */
105    public AstClafer refTo(AstClafer targetType) {
106        if (hasRef()) {
107            throw new IllegalArgumentException(this + " already has a ref");
108        }
109        this.ref = new AstRef(this, targetType, false);
110        return this;
111    }
112
113    /**
114     * Set this Clafer's reference to target type along with a uniqueness
115     * constraint.
116     *
117     * @param targetType the type to refer to
118     * @return this Clafer
119     */
120    public AstClafer refToUnique(AstClafer targetType) {
121        if (hasRef()) {
122            throw new IllegalArgumentException(this + " already has a ref");
123        }
124        this.ref = new AstRef(this, targetType, true);
125        return this;
126    }
127
128    /**
129     * Returns this Clafer's group cardinality.
130     *
131     * @return this Clafer's group cardinality
132     */
133    public Card getGroupCard() {
134        return groupCard;
135    }
136
137    /**
138     * Set this Clafer's group cardinality.
139     *
140     * @param groupCard the group cardinality
141     * @return this Clafer
142     */
143    public AstClafer withGroupCard(Card groupCard) {
144        this.groupCard = Check.notNull(groupCard);
145        return this;
146    }
147
148    /**
149     * Set this Clafer's low group cardinality and set the high group
150     * cardinality to unbounded.
151     *
152     * @param low the low group cardinality
153     * @return this Clafer
154     */
155    public AstClafer withGroupCard(int low) {
156        return withGroupCard(new Card(low));
157    }
158
159    /**
160     * Set this Clafer's group cardinality.
161     *
162     * @param low the low group cardinality
163     * @param high the high group cardinality
164     * @return this Clafer
165     */
166    public AstClafer withGroupCard(int low, int high) {
167        return withGroupCard(new Card(low, high));
168    }
169
170    /**
171     * Checks if this Clafer has any concrete children.
172     *
173     * @return {@code true} if and only if this Clafer has children,
174     * {@code false} otherwise
175     */
176    public boolean hasChildren() {
177        return !children.isEmpty();
178    }
179
180    /**
181     * Returns this Clafer's concrete children
182     *
183     * @return this Clafer's concrete children
184     */
185    public List<AstConcreteClafer> getChildren() {
186        return Collections.unmodifiableList(children);
187    }
188
189    /**
190     * Add a new concrete child under this Clafer.
191     *
192     * @param name the name of the child
193     * @return the new child
194     */
195    public AstConcreteClafer addChild(String name) {
196        AstConcreteClafer child = new AstConcreteClafer(name, this, claferClafer);
197        children.add(child);
198        return child.extending(claferClafer);
199    }
200
201    /**
202     * Checks if this Clafer has any constraints.
203     *
204     * @return {@code true} if and only if this Clafer has constraints,
205     * {@code false} otherwise
206     */
207    public boolean hasConstraints() {
208        return !constraints.isEmpty();
209    }
210
211    /**
212     * Returns this Clafer's constraints.
213     *
214     * @return this Clafer's constraints
215     */
216    public List<AstConstraint> getConstraints() {
217        return Collections.unmodifiableList(constraints);
218    }
219
220    /**
221     * Add a new constraint under this Clafer.
222     *
223     * @param expr a tautology
224     * @return the constraint
225     */
226    public AstConstraint addConstraint(AstBoolExpr expr) {
227        AstConstraint constraint = new AstConstraint(this, expr);
228        constraints.add(constraint);
229        return constraint;
230    }
231
232    /**
233     * {@inheritDoc}
234     */
235    @Override
236    public boolean equals(Object obj) {
237        return this == obj;
238    }
239
240    /**
241     * {@inheritDoc}
242     */
243    @Override
244    public int hashCode() {
245        return getName().hashCode();
246    }
247
248    /**
249     * {@inheritDoc}
250     */
251    @Override
252    public String toString() {
253        return getName();
254    }
255}