/*
* xtc - The eXTensible Compiler
* Copyright (C) 2007 Robert Grimm
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.type;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import xtc.util.Utilities;
/**
* A variant type. Variants can be monomorphic or polymorphic, with
* the latter possibly sharing tuples with other variants.
* Monomorphic variants must be named and are compared through name
* equivalence. Polymorphic variants may be anonymous and are
* compared through structural equivalence.
*
* @author Robert Grimm
* @version $Revision: 1.12 $
*/
public class VariantT extends DerivedT {
/** The qualified name. */
final private String qname;
/** The qualifier. */
final private String qualifier;
/** The simple name. */
final private String sname;
/** The flag for whether the variant is polymorphic. */
final private boolean polymorphic;
/** The list of tuples. */
private List<TupleT> tuples;
/**
* Create a new incomplete variant type. The new variant type is
* not polymorphic.
*
* @param name The name.
* @throws NullPointerException Signals that the name is null.
*/
public VariantT(String name) {
this(null, name, false, null);
}
/**
* Create a new variant type. The new variant type is not
* polymorphic.
*
* @param name The name.
* @param tuples The tuples.
* @throws NullPointerException Signals that the name is null.
*/
public VariantT(String name, List<TupleT> tuples) {
this(null, name, false, tuples);
}
/**
* Create a new variant type. Note that polymorphic variants may be
* anonymous, i.e., have a null name.
*
* @param name The name.
* @param polymorphic The flag for polymorphic variants.
* @param tuples The tuples.
* @throws NullPointerException Signals that the name is null for a
* monomorphic variant.
*/
public VariantT(String name, boolean polymorphic, List<TupleT> tuples) {
this(null, name, polymorphic, tuples);
}
/**
* Create a new variant type.
*
* @param template The type whose annotations to copy.
* @param name The name.
* @param polymorphic The flag for polymorphic variants.
* @param tuples The tuples.
* @throws NullPointerException Signals that the name is null for a
* monomorphic variant.
*/
public VariantT(Type template, String name, boolean polymorphic,
List<TupleT> tuples) {
super(template);
this.qname = name;
this.qualifier = (null == name) ? null : Utilities.getQualifier(name);
this.sname = (null == name) ? null : Utilities.unqualify(name);
this.polymorphic = polymorphic;
this.tuples = tuples;
if (null == name && ! polymorphic) {
throw new NullPointerException("Null name");
}
}
public Type seal() {
if (! isSealed()) {
super.seal();
tuples = Type.seal(tuples);
}
return this;
}
public VariantT copy() {
return new VariantT(this, qname, polymorphic, copy(tuples));
}
public Type.Tag tag() {
return Type.Tag.VARIANT;
}
public boolean isVariant() {
return true;
}
public VariantT toVariant() {
return this;
}
/**
* Get this variant's name.
*
* @return The name or <code>null</code> if this variant is anonymous.
*/
public String getName() {
return qname;
}
/**
* Get this variant's qualifier.
*
* @return The qualifier or <code>null</code> if this variant does
* not have a qualified name.
*/
public String getQualifier() {
return qualifier;
}
/**
* Get this variant's simple name.
*
* @return The simple name or <code>null</code> if this variant is
* anonymous.
*/
public String getSimpleName() {
return sname;
}
/**
* Determine whether the variant is polymorphic.
*
* @return <code>true</code> if the variant is polymorphic.
*/
public boolean isPolymorphic() {
return polymorphic;
}
/**
* Look up the tuple with the specified name.
*
* @param name The name.
* @return The tuple or {@link ErrorT#TYPE} if this variant has no
* such tuple.
*/
public Type lookup(String name) {
for (TupleT tuple : tuples) if (tuple.hasName(name)) return tuple;
return ErrorT.TYPE;
}
/**
* Look up the tuple with the specified simple name.
*
* @param name The simple name.
* @return The first such tuple or {@link ErrorT#TYPE} if this
* variant has no such tuple.
*/
public Type lookupSimple(String name) {
for (TupleT tuple : tuples) if (tuple.hasSimpleName(name)) return tuple;
return ErrorT.TYPE;
}
/**
* Get this variant's tuples.
*
* @return The list of tuples.
*/
public List<TupleT> getTuples() {
return tuples;
}
/**
* Set this variant's tuples.
*
* @param tuples The new list of tuples.
*/
public void setTuples(List<TupleT> tuples) {
checkNotSealed();
this.tuples = tuples;
}
public int hashCode() {
if (polymorphic) {
return null == tuples ? 0 : tuples.hashCode();
} else {
return qname.hashCode();
}
}
/**
* Determine whether this variant equals the specified object. This
* method implements name equivalence for non-polymorphic variants
* and structural equivalence for polymorphic variants.
*
* @param o The object.
* @return <code>true</code> if this variant equals the object.
*/
public boolean equals(Object o) {
if (! (o instanceof Type)) return false;
Type t = resolve(o);
if (this == t) return true;
if (! t.isVariant()) return false;
VariantT other = t.toVariant();
if (polymorphic != other.polymorphic) return false;
if (! polymorphic) return qname.equals(other.qname);
if (null == tuples) return null == other.tuples;
if (tuples.size() != other.tuples.size()) return false;
for (TupleT tuple : tuples) {
if (! other.tuples.contains(tuple)) return false;
}
for (TupleT tuple : other.tuples) {
if (! tuples.contains(tuple)) return false;
}
return true;
}
public void write(Appendable out) throws IOException {
if (null != qname) {
out.append("variant ");
out.append(qname);
} else {
out.append("variant(");
for (Iterator<TupleT> iter = tuples.iterator(); iter.hasNext(); ) {
out.append('`');
iter.next().write(out);
if (iter.hasNext()) out.append(", ");
}
out.append(')');
}
}
}