SymbolicExpression.java

package edu.udel.cis.vsl.tass.symbolic.expression;

import java.util.Map;

import edu.udel.cis.vsl.tass.symbolic.IF.SymbolicExpressionIF;
import edu.udel.cis.vsl.tass.symbolic.IF.type.SymbolicTypeIF;

/**
 * The root of the symbolic expression hierarchy. Every symbolic expression
 * extends this class.
 * 
 * This class is implemented using the Flyweight Pattern. Because of this, there
 * is no need to override the equals or hashCode methods of Object.
 * 
 * The intrinsicHashCode and intrinsicEquals methods are use for internal
 * purposes. They are used by the symbolic.real module to implement the
 * flyweight pattern. They are not revealed to the outside user (who will only
 * access this class through the SymbolicExpressionIF interface). The user will
 * use the standard hashCode() and equals() methods defined in Object. This is
 * OK, because of the Flyweight Pattern guarantees the user will only get one
 * unique instance from each equivalence class of objects in this class, the
 * equivalence relation being defined by intrinsicEquals.
 * 
 */
public abstract class SymbolicExpression implements SymbolicExpressionIF {

	/**
	 * This keeps track of the number of unique (up to equivalence) symbolic
	 * expressions generated. It is incremented when the flyweight method is
	 * called, if the expression does not already exist in the flyweight table.
	 */
	private static int instanceCounter = 0;

	/**
	 * Every committed symbolic expression has a non-negative id, which is
	 * unique among all symbolic expressions. This is that ID. It is set once
	 * the flyweight method is called. Until then, it is -1.
	 * 
	 */
	public int uniqueId = -1;

	/**
	 * The type of this symbolic expression.
	 */
	private SymbolicTypeIF type;

	/**
	 * Every symbolic expression also has a user-settable id, which can be used
	 * any way the user wants.
	 */
	private int id = -1;

	/**
	 * Creates a new symbolic expression constant with the given type.
	 */
	protected SymbolicExpression(SymbolicTypeIF type) {
		this.type = type;
	}

	/**
	 * Returns the id (non-unique).
	 */
	public int id() {
		return id;
	}

	/**
	 * Sets the id (non-unique).
	 * 
	 * @param id
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * Returns the type of this symbolic expression.
	 */
	public SymbolicTypeIF type() {
		return type;
	}

	/**
	 * Compares two committed symbolic expressions. There is a total order on
	 * the committed symbolic expressions given by the unique IDs.
	 * 
	 * @param x
	 *            the first symbolic expression
	 * @param y
	 *            the second symbolic expression
	 * @return 0 if they are equal, -1 if the second come before the first, +1
	 *         otherwise
	 */
	public static int compare(SymbolicExpression x, SymbolicExpression y) {
		return x.uniqueId - y.uniqueId;
	}

	/**
	 * Each concrete class must implement this method and the intrinsic equals
	 * method, which define hash codes and equals methods based on the intrinsic
	 * characteristics of the symbolic expressions in that class.
	 */
	protected abstract int intrinsicHashCode();

	protected abstract boolean intrinsicEquals(SymbolicExpression that);

	/**
	 * Implements the flyweight pattern: if there already exists a committed
	 * symbolic expression which is equivalent to the given expression
	 * (according to intrinsicEquals) returns that one, otherwise, commits
	 * expression (adds to table), assigns it a new unique id, and returns it.
	 * 
	 * @param map
	 *            the map used to record the committed expressions, one from
	 *            each equivalence class
	 * @param expression
	 *            the symbolic expression to be flyweighted
	 * @return the unique representative of the equivalence class of expression,
	 *         which may be expression itself
	 */
	public static <T extends SymbolicExpression> T flyweight(
			Map<SymbolicExpressionKey<T>, T> map, T expression) {
		SymbolicExpressionKey<T> key = new SymbolicExpressionKey<T>(expression);
		T old = map.get(key);

		if (old == null) {
			expression.uniqueId = instanceCounter++;
			map.put(key, expression);
			return expression;
		}
		return old;
	}

	/**
	 * Hash code: unique id. Note this is only effective if invoked on committed
	 * expressions.
	 */
	@Override
	public int hashCode() {
		return uniqueId;
	}
}