CommonStructOrUnionType.java

/**
 * 
 */
package edu.udel.cis.vsl.civl.model.common.type;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import edu.udel.cis.vsl.civl.model.IF.CIVLException;
import edu.udel.cis.vsl.civl.model.IF.CIVLInternalException;
import edu.udel.cis.vsl.civl.model.IF.CIVLSource;
import edu.udel.cis.vsl.civl.model.IF.Identifier;
import edu.udel.cis.vsl.civl.model.IF.type.CIVLPrimitiveType;
import edu.udel.cis.vsl.civl.model.IF.type.CIVLStructOrUnionType;
import edu.udel.cis.vsl.civl.model.IF.type.CIVLType;
import edu.udel.cis.vsl.civl.model.IF.type.StructOrUnionField;
import edu.udel.cis.vsl.civl.model.common.CommonIdentifier;
import edu.udel.cis.vsl.sarl.IF.SymbolicUniverse;
import edu.udel.cis.vsl.sarl.IF.type.SymbolicType;

/**
 * @author zirkel
 * 
 */
public class CommonStructOrUnionType extends CommonType
		implements
			CIVLStructOrUnionType {

	private boolean isStruct;

	private boolean isHandleObject;

	private Identifier name;

	private StructOrUnionField[] fields = null;

	/**
	 * Create a new (incomplete) struct or union.
	 * 
	 * @param name
	 *            The name of this struct or union.
	 * @param isStruct
	 *            True if a struct, false if a union.
	 */
	public CommonStructOrUnionType(Identifier name, boolean isStruct) {
		this.name = name;
		this.isStruct = isStruct;
		this.isHandleObject = false;
		// fields = new StructOrUnionField[0];
	}

	@Override
	public Iterable<StructOrUnionField> fields() {
		return Arrays.asList(fields);
	}

	@Override
	public Identifier name() {
		return name;
	}

	@Override
	public String toString() {
		if (this.isStruct)
			return "struct " + name.toString();
		else
			return "union " + name.toString();
	}

	@Override
	public int numFields() {
		if (fields == null)
			return 0;
		return fields.length;
	}

	@Override
	public StructOrUnionField getField(int index) {
		return fields[index];
	}

	@Override
	public boolean hasState() {
		if (!isComplete())
			return false;
		// throw new CIVLInternalException(
		// (isStruct ? "Struct" : "Union") + " not complete",
		// (CIVLSource) null);
		for (StructOrUnionField field : fields) {
			if (field.type().hasState())
				return true;
		}
		return false;
	}

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

	@Override
	public void complete(Collection<StructOrUnionField> fields) {
		if (isComplete())
			throw new CIVLInternalException(
					(isStruct ? "Struct" : "Union") + " already complete",
					(CIVLSource) null);
		else {
			int numFields = fields.size();
			int count = 0;

			this.fields = new StructOrUnionField[numFields];
			for (StructOrUnionField field : fields) {
				this.fields[count] = field;
				count++;
			}
		}
	}

	@Override
	public void complete(StructOrUnionField[] fields) {
		if (isComplete())
			throw new CIVLInternalException(
					(isStruct ? "Struct" : "Union") + "  already complete",
					(CIVLSource) null);
		else {
			int numFields = fields.length;
			int count = 0;

			this.fields = new StructOrUnionField[numFields];
			for (StructOrUnionField field : fields) {
				this.fields[count] = field;
				count++;
			}
		}
	}

	@Override
	public SymbolicType getDynamicType(SymbolicUniverse universe) {
		if (dynamicType == null) {
			if (!isComplete())
				throw new CIVLInternalException(
						"cannot get dynamic type of incomplete "
								+ (isStruct ? "struct" : "union") + " type: "
								+ this,
						(CIVLSource) null);
			else {
				LinkedList<SymbolicType> fieldDynamicTypes = new LinkedList<SymbolicType>();

				for (StructOrUnionField field : fields) {
					SymbolicType fieldDynamicType = field.type()
							.getDynamicType(universe);

					fieldDynamicTypes.add(fieldDynamicType);
				}
				if (this.isStruct) {
					dynamicType = universe.tupleType(
							universe.stringObject(name.name()),
							fieldDynamicTypes);
				} else {
					try {
						dynamicType = universe.unionType(
								universe.stringObject(name.name()),
								fieldDynamicTypes);
					} catch (IllegalArgumentException ex) {
						throw new CIVLException(ex.getMessage(), null);
					}
				}
			}
		}
		return dynamicType;
	}

	@Override
	public boolean isStructType() {
		return isStruct;
	}

	@Override
	public boolean isUnionType() {
		return !isStruct;
	}

	// @Override
	// public boolean isHandleObjectType() {
	// return this.isHandleObject;
	// }

	@Override
	public void setHandleObjectType(boolean value) {
		this.isHandleObject = value;
	}

	@Override
	public int getFieldIndex(String field) {
		for (int i = 0; i < fields.length; i++) {
			String name = fields[i].name().name();

			if (name.equals(field))
				return i;
		}
		return -1;
	}

	@Override
	public TypeKind typeKind() {
		return TypeKind.STRUCT_OR_UNION;
	}

	@Override
	public CIVLType copyAs(CIVLPrimitiveType type, SymbolicUniverse universe) {
		String newName = "CIVL" + name;
		Identifier newId = new CommonIdentifier(name.getSource(),
				universe.stringObject(newName));
		CIVLStructOrUnionType newType = new CommonStructOrUnionType(newId,
				isHandleObject);
		List<StructOrUnionField> newFields = new ArrayList<>(this.numFields());

		for (StructOrUnionField field : fields)
			newFields.add(field.copyAs(type, universe));
		newType.complete(newFields);
		return newType;
	}

	@Override
	public boolean areSubtypesScalar() {
		for (StructOrUnionField field : this.fields) {
			if (!field.type().isScalar())
				return false;
		}
		return true;
	}
}