CommonSymbolicObject.java
/*******************************************************************************
* Copyright (c) 2013 Stephen F. Siegel, University of Delaware.
*
* This file is part of SARL.
*
* SARL is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SARL 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SARL. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package edu.udel.cis.vsl.sarl.object.common;
import edu.udel.cis.vsl.sarl.IF.number.RationalNumber;
import edu.udel.cis.vsl.sarl.IF.object.SymbolicObject;
import edu.udel.cis.vsl.sarl.object.IF.ObjectFactory;
/**
* A partial implementation of {@link SymbolicObject}.
*
* @author siegel
*/
public abstract class CommonSymbolicObject implements SymbolicObject {
// static fields ...
/**
* Used as an ID number, indicates this is a new object that has not even
* been hashed yet.
*/
private final static int NOT_HASHED = -3;
/**
* Used as an ID number, indicates this object has been hashed (and its hash
* code cached in variable {@link #hashCode}, but not yet canonicalized.
*/
private final static int HASHED = -2;
/**
* Used as an ID number, indicates that this object is in the midst of being
* canonicalized. Needed to avoid infinite recursion when canonicalizing
* children.
*/
private final static int IN_CANONIC = -1;
/**
* If true, more detailed string representations of symbolic objects will be
* returned by the {@link #toString()} method.
*/
private final static boolean debug = false;
// instance fields ...
/**
* Cached hashCode, set upon first run of {@link #hashCode()}.
*/
private int hashCode;
/**
*
* The unique nonnegative canonic ID number OR a negative integer indicating
* the current status of a non-canonic object.
*
* <p>
* If this object has not been hashed, {@link #id} will be
* {@link #NOT_HASHED}. If this object has been hashed, but is not canonic,
* {@link #id} will be {@link #HASHED}. If this object is canonic (which
* implies it has been hashed), {@link #id} will be nonnegative and will be
* the unique ID number among canonic objects. This means this object is the
* unique representative of its equivalence class.
* </p>
*/
private int id = NOT_HASHED;
/**
* This number is typically used to place a total order on certain canonic
* objects, e.g., to "cache" comparisons. This class simply provides a
* getter and setter for this field; it is up to clients on how to use it.
*/
private RationalNumber order;
// Constructors...
/**
* Instantiates this symbolic object, with {@link #id} initialized to
* {@link #NOT_HASHED}.
*/
protected CommonSymbolicObject() {
}
// Abstract Methods...
/**
* Is the given symbolic object equal to this one---assuming the given
* symbolic object is of the same kind as this one? Must be defined in any
* concrete subclass.
*
* @param that
* a symbolic object of the same kind as this one
* @return true iff they define the same type
*/
protected abstract boolean intrinsicEquals(SymbolicObject o);
/**
* Canonizes the children of this symbolic object. Replaces each child with
* the canonic version of that child.
*
* @param factory
* the object factory that is responsible for this symbolic
* object
*/
protected abstract void canonizeChildren(ObjectFactory factory);
/**
* Computes the hash code to be returned by hashCode(). This is run the
* first time hashCode is run. The hash is cached for future calls to
* hashCode();
*
* @return hash code
*/
protected abstract int computeHashCode();
// private concrete methods...
/**
* Has this object been hashed (and therefore had its hash code cached in
* {@link #hashCode}).
*
* @return <code>true</code> iff this has been hashed
*/
private boolean hashed() {
return id >= HASHED;
}
/**
* Sets the {@link #id} to {@link #HASHED} to indicate that this object has
* been hashed.
*/
private void setHashed() {
id = HASHED;
}
// package-private concrete methods...
/**
* Sets the id number of this object.
*
* @param id
* the new ID number; should be nonnegative
*/
void setId(int id) {
this.id = id;
}
// protected concrete methods...
/**
* Places parentheses around the string buffer.
*
* @param buffer
* a string buffer
*/
protected void atomize(StringBuffer buffer) {
buffer.insert(0, '(');
buffer.append(')');
}
// public methods: SymbolicObject
@Override
public boolean isCanonic() {
return id >= IN_CANONIC;
}
@Override
public void setInCanonic() {
this.id = IN_CANONIC;
}
@Override
public int id() {
return id;
}
@Override
public void setOrder(RationalNumber r) {
this.order = r;
}
@Override
public RationalNumber getOrder() {
return order;
}
// public methods: Object
@Override
public int hashCode() {
if (!hashed()) {
hashCode = computeHashCode();
setHashed();
}
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o instanceof CommonSymbolicObject) {
CommonSymbolicObject that = (CommonSymbolicObject) o;
if (id >= 0 && that.id >= 0) // both are canonic reps
return false;
if (hashCode() != that.hashCode())
return false;
if (this.symbolicObjectKind() != that.symbolicObjectKind())
return false;
return intrinsicEquals(that);
}
return false;
}
@Override
public String toString() {
if (debug)
return toStringBufferLong().toString();
else
return toStringBuffer(false).toString();
}
}