CommonEnumerationType.java

package edu.udel.cis.vsl.abc.ast.type.common;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Map;

import edu.udel.cis.vsl.abc.ast.entity.IF.CommonEntity;
import edu.udel.cis.vsl.abc.ast.entity.IF.Entity;
import edu.udel.cis.vsl.abc.ast.entity.IF.ProgramEntity;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.DeclarationNode;
import edu.udel.cis.vsl.abc.ast.type.IF.EnumerationType;
import edu.udel.cis.vsl.abc.ast.type.IF.Enumerator;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
import edu.udel.cis.vsl.abc.ast.value.IF.Value;
import edu.udel.cis.vsl.abc.err.IF.ABCRuntimeException;

/**
 * Implementation of {@link EnumerationType}. The {@link Entity} methods are
 * implemented using the Delegation Pattern by delegating to an {@link Entity}
 * object which is a field of this class.
 * 
 * @author siegel
 * 
 */
public class CommonEnumerationType extends CommonIntegerType implements
		EnumerationType {

	private final static int classCode = CommonEnumerationType.class.hashCode();

	private ProgramEntity entity;

	private Object key;

	private String tag;

	private ArrayList<Enumerator> enumerators = null;

	public CommonEnumerationType(Object key, String tag) {
		super(TypeKind.ENUMERATION);
		assert key != null;
		this.key = key;
		this.tag = tag;
		this.entity = new CommonEntity(EntityKind.ENUMERATION, tag,
				ProgramEntity.LinkageKind.NONE);
	}

	@Override
	public String getTag() {
		return tag;
	}

	@Override
	public int getNumEnumerators() {
		if (!isComplete())
			throw new RuntimeException("Enumeration type " + tag
					+ " is incomplete");
		return enumerators.size();
	}

	@Override
	public Enumerator getEnumerator(int index) {
		if (!isComplete())
			throw new ABCRuntimeException("Enumeration type " + tag
					+ " is incomplete");
		return enumerators.get(index);
	}

	@Override
	public Iterable<Enumerator> getEnumerators() {
		if (!isComplete())
			throw new ABCRuntimeException("Enumeration type " + tag
					+ " is incomplete");
		return enumerators;
	}

	@Override
	public boolean isComplete() {
		return enumerators != null;
	}

	public void complete(Iterable<Enumerator> enumeratorList) {
		if (isComplete())
			throw new ABCRuntimeException("Enumerator type " + tag
					+ " is already complete");
		enumerators = new ArrayList<>();
		for (Enumerator enumerator : enumeratorList)
			enumerators.add(enumerator);
	}

	@Override
	public boolean isEnumeration() {
		return true;
	}

	/**
	 * Returns the string that is used to check compatibility of this tag with
	 * another tag. This removes any suffix beginning with <code>$TU</code>. In
	 * CIVL-C semantics, such a suffix is ignored for the purposes of checking
	 * compatibility of two tags. It is used by CIVL to give unique names to
	 * tagged entities in different translation units so they can be merged into
	 * a single AST, but such entities should still be considered compatible.
	 * 
	 * NOTE: also removing $anon.
	 * 
	 * @return the tag with any suffix beginning with '$' removed
	 */
	private String getCompatibilityString() {
		if (tag == null)
			return "";
		if (tag.startsWith("$anon")) {
			// "$anon" prefixes are inserted by ABC to give names
			// to anonymous structures. Since the original tag
			// was null, that is what should be used for checking
			// compatibility...
			return "";
		} else {
			int dollarIndex = tag.indexOf("$TU");

			return dollarIndex < 0 ? tag : tag.substring(0, dollarIndex);
		}
	}

	@Override
	public boolean compatibleWith(Type type) {
		if (this == type)
			return true;
		if (type instanceof CommonEnumerationType) {
			CommonEnumerationType that = (CommonEnumerationType) type;

			if (tag == null) {
				if (that.tag != null)
					return false;
			} else if (!getCompatibilityString().equals(
					that.getCompatibilityString()))
				return false;
			if (enumerators == null) {
				if (that.enumerators != null)
					return false;
			} else {
				int numEnumerators = enumerators.size();

				if (that.enumerators == null)
					return false;
				if (numEnumerators != that.enumerators.size())
					return false;
				for (int i = 0; i < numEnumerators; i++) {
					Enumerator enum1 = enumerators.get(i);
					Enumerator enum2 = that.enumerators.get(i);

					if (enum1 == null) {
						if (enum2 != null)
							return false;
					} else {
						String name1 = enum1.getName(), name2;
						Value value1 = enum1.getValue(), value2;

						if (enum2 == null)
							return false;
						name2 = enum2.getName();
						if (name1 == null) {
							if (name2 != null)
								return false;
						} else if (!name1.equals(name2))
							return false;
						value2 = enum2.getValue();
						if (value1 == null) {
							if (value2 != null)
								return false;
						} else if (!value1.equals(value2))
							return false;
					}
				}
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean equivalentTo(Type type) {
		if (this == type)
			return true;
		if (type instanceof CommonEnumerationType) {
			CommonEnumerationType that = (CommonEnumerationType) type;

			if (tag == null) {
				if (that.tag != null)
					return false;
			} else if (!getCompatibilityString().equals(
					that.getCompatibilityString()))
				return false;
			if (enumerators == null) {
				if (that.enumerators != null)
					return false;
			} else {
				int numEnumerators = enumerators.size();

				if (that.enumerators == null)
					return false;
				if (numEnumerators != that.enumerators.size())
					return false;
				for (int i = 0; i < numEnumerators; i++) {
					Enumerator enum1 = enumerators.get(i);
					Enumerator enum2 = that.enumerators.get(i);

					if (enum1 == null) {
						if (enum2 != null)
							return false;
					} else {
						String name1 = enum1.getName(), name2;
						Value value1 = enum1.getValue(), value2;

						if (enum2 == null)
							return false;
						name2 = enum2.getName();
						if (name1 == null) {
							if (name2 != null)
								return false;
						} else if (!name1.equals(name2))
							return false;
						value2 = enum2.getValue();
						if (value1 == null) {
							if (value2 != null)
								return false;
						} else if (!value1.equals(value2))
							return false;
					}
				}
				return true;
			}
		}
		return false;
	}

	@Override
	public void print(String prefix, PrintStream out, boolean abbrv) {
		out.print("Enumeration[tag=" + tag + ", key=" + key + "]");
		if (!abbrv && enumerators != null) {
			for (Enumerator enumerator : enumerators) {
				out.println();
				out.print(prefix + "| " + enumerator.getName());
			}
		}
	}

	@Override
	public boolean isInteger() {
		return true;
	}

	@Override
	public Object getKey() {
		return key;
	}

	@Override
	public void clear() {
		enumerators = null;
	}

	@Override
	public boolean equals(Object object) {
		if (this == object)
			return true;
		if (object instanceof CommonEnumerationType) {
			CommonEnumerationType that = (CommonEnumerationType) object;

			return key.equals(that.key)
					&& (tag == null || tag.equals(that.tag));
		}
		return false;
	}

	@Override
	public int hashCode() {
		int result = classCode ^ key.hashCode();

		if (tag != null)
			result ^= tag.hashCode();
		return result;
	}

	@Override
	public EntityKind getEntityKind() {
		return entity.getEntityKind();
	}

	@Override
	public String getName() {
		return tag;
	}

	@Override
	public Iterable<DeclarationNode> getDeclarations() {
		return entity.getDeclarations();
	}

	@Override
	public DeclarationNode getFirstDeclaration() {
		return entity.getFirstDeclaration();
	}

	@Override
	public int getNumDeclarations() {
		return entity.getNumDeclarations();
	}

	@Override
	public DeclarationNode getDeclaration(int index) {
		return entity.getDeclaration(index);
	}

	@Override
	public void addDeclaration(DeclarationNode declaration) {
		entity.addDeclaration(declaration);
	}

	@Override
	public DeclarationNode getDefinition() {
		return entity.getDefinition();
	}

	@Override
	public void setDefinition(DeclarationNode declaration) {
		entity.setDefinition(declaration);
	}

	@Override
	public ProgramEntity.LinkageKind getLinkage() {
		return entity.getLinkage();
	}

	@Override
	public void setLinkage(ProgramEntity.LinkageKind linkage) {
		if (linkage != ProgramEntity.LinkageKind.NONE)
			throw new ABCRuntimeException("Linkage of enumeration must be NONE");
	}

	@Override
	public EnumerationType getType() {
		return this;
	}

	@Override
	public void setType(Type type) {
		if (type != this)
			throw new ABCRuntimeException("Cannot change type of enumeration");
	}

	@Override
	public boolean isSystem() {
		return entity.isSystem();
	}

	@Override
	public void setIsSystem(boolean value) {
		entity.setIsSystem(value);
	}

	@Override
	protected boolean similar(Type other, boolean equivalent,
			Map<TypeKey, Type> seen) {
		return equivalent ? equivalentTo(other) : compatibleWith(other);
	}

	@Override
	public String toString() {
			return "enum " + tag;
	}
}