MorphicObject.java

package edu.udel.cis.vsl.tass.morph;

/**
 * A simple, partial implementation of Morphic, intended to be extended.
 * 
 * @author siegel
 * 
 */
public abstract class MorphicObject implements Morphic {

	/** Has this component been committed? */
	private boolean isCommitted = false;

	/**
	 * Is this a canonic version of this component? Invariant: isCanonic =>
	 * isCommitted.
	 */
	private boolean isCanonic = false;

	/**
	 * Has the hash code been computed and cached since this object was
	 * committed? Invariant: hashed => isCommitted.
	 */
	private boolean hashed = false;

	/** The cached hashCode, which is used only if hashed is true. */
	private int hashCode = -1;

	/**
	 * Determines whether the given component is equivalent to this one without
	 * relying on any special knowledge such as whether the objects are
	 * canonical. This method should be defined in the subclass. It does the
	 * "real work", comparing fields, and so on.
	 */
	protected abstract boolean computeEquals(Morphic component);

	/**
	 * Computes and returns the hash code for this component. Must be overridden
	 * by the subclass.
	 */
	protected abstract int computeHashCode();

	/**
	 * An optimized equals methods. If the two objects are canonical components,
	 * can use ==.
	 */
	@Override
	public boolean equals(Object object) {
		if (this == object)
			return true;
		if (object instanceof MorphicObject) {
			MorphicObject that = (MorphicObject) object;

			if (isCanonic && that.isCanonic)
				return false;
			if (hashed && that.hashed && hashCode != that.hashCode)
				return false;
			return computeEquals(that);
		}
		return false;
	}

	/**
	 * An optimized hashCode function. If the object is committed and the hash
	 * code has been cached, returns the cached copy.
	 */
	@Override
	public int hashCode() {
		if (hashed) {
			return hashCode;
		} else {
			int result = computeHashCode();

			if (isCommitted) {
				hashed = true;
				hashCode = result;
			}
			return result;
		}
	}

	/**
	 * Sets the commit bit to true and commits all children.
	 */
	@Override
	public void commit() {
		if (isCommitted)
			return;
		isCommitted = true;
		commitChildren();
	}

	protected abstract void commitChildren();

	/**
	 * Sets the canonical bit to true. Requires that the object is already
	 * committed.
	 */
	protected void canonicalize() {
		assert isCommitted;
		isCanonic = true;
	}

	@Override
	public boolean isCanonic() {
		return isCanonic;
	}

	@Override
	public boolean isCommitted() {
		return isCommitted;
	}

	@Override
	public String descriptor() {
		if (!isCommitted) {
			return "";
		}
		if (!isCanonic()) {
			return "(committed)";
		}
		return "(canonic)";
	}
}