blob: ef28ad406c747fc729ec47213dea78c9b9ef4c77 [file] [log] [blame]
package com.googlecode.prolog_cafe.lang;
import com.googlecode.prolog_cafe.exceptions.InternalException;
/**
* Variable.<br>
* The <code>VariableTerm</code> class represents a logical variable.<br>
* For example,
* <pre>
* Term t = new VariableTerm();
* </pre>
*
* @author Mutsunori Banbara (banbara@kobe-u.ac.jp)
* @author Naoyuki Tamura (tamura@kobe-u.ac.jp)
* @version 1.0
*/
public class VariableTerm extends Term implements Undoable {
/** Holds a term to which this variable is bound. Initial value is <code>this</code> (self-reference). */
private Term val;
/** A CPF time stamp when this object is newly constructed. */
private long timeStamp;
/** Constructs a new logical variable so that
* the <code>timeStamp</code> field is set to <code>Long.MIN_VALUE</code>.
*/
public VariableTerm() {
val = this;
timeStamp = Long.MIN_VALUE;
}
/** Constructs a new logical variable so that
* the <code>timeStamp</code> field is set to the current value of
* <code>CPFTimeStamp</code> of the specified Prolog engine.
* @param engine Current Prolog engine.
* @see Prolog#getCPFTimeStamp
*/
public VariableTerm(Prolog engine) {
val = this;
timeStamp = engine.getCPFTimeStamp();
}
/** Returns a string representation of this object.*/
protected String variableName() { return "_" + Integer.toHexString(hashCode()).toUpperCase(); }
/* Term */
/**
* Checks whether the argument term is unified with this one.
* If this is an unbound variable, the <code>unify</code> method binds this to
* the dereferenced value of argument term: <code>bind(t.dereference(), trail)</code>,
* and returns <code>true</code>.
* Otherwise, it returns a <code>boolean</code> whose value is <code>val.unify(t, trail)</code>.
* @param t the term to be unified with.
* @param trail Trail Stack.
* @return <code>true</code> if succeeds, otherwise <code>false</code>.
* @see #val
* @see #bind(Term,Trail)
* @see Trail
*/
public boolean unify(Term t, Trail trail) {
if (val != this)
return val.unify(t, trail);
t = t.dereference();
if (this != t)
bind(t, trail);
return true;
}
/**
* Binds this variable to a given term.
* And pushs this variable to trail stack if necessary.
* @param t a term to be bound.
* @param trail Trail Stack
* @see Trail
*/
public void bind(Term t, Trail trail) {
if (t.isVariable()) {
VariableTerm v = (VariableTerm) t;
if (v.timeStamp >= this.timeStamp) {
v.val = this;
if (v.timeStamp < trail.timeStamp)
trail.push(v);
return;
}
}
val = t;
if (timeStamp < trail.timeStamp)
trail.push(this);
}
/**
* Checks whether this object is convertible with the given Java class type
* if this variable is unbound.
* Otherwise, returns the value of <code>val.convertible(type)</code>.
* @param type the Java class type to compare with.
* @return <code>true</code> if this (or dereferenced term) is
* convertible with <code>type</code>. Otherwise <code>false</code>.
* @see #val
*/
public boolean convertible(Class type) {
if (val != this)
return val.convertible(type);
return convertible(this.getClass(), type);
}
/**
* Returns a copy of this object if unbound variable.
* Otherwise, returns the value of <code>val.copy(engine)</code>.
* @see #val
*/
protected Term copy(Prolog engine) {
VariableTerm co;
if (val != this)
return val.copy(engine);
co = engine.copyHash.get(this);
if (co == null) {
// co = new VariableTerm(engine);
co = new VariableTerm();
engine.copyHash.put(this, co);
}
return co;
}
public Term dereference() {
if (val == this)
return this;
return val.dereference();
}
public boolean isGround() {
if (val != this)
return val.isGround();
return false;
}
public String name() {
if (val == this)
return "";
return val.dereference().name();
}
/**
* Returns <code>this</code> if this variable is unbound.
* Otherwise, returns a Java object that corresponds to the dereferenced term:
* <code>val.toJava()</code>.
* @return a Java object defined in <em>Prolog Cafe interoperability with Java</em>.
* @see #val
*/
public Object toJava() {
if (val != this)
return val.toJava();
return this;
}
/**
* Returns a quoted string representation of this term if unbound.
* Otherwise, returns the value of dereferenced term:
* <code>val.toQuotedString()</code>
* @see #val
*/
public String toQuotedString() {
if (val != this)
return val.toQuotedString();
return variableName();
}
/* Object */
/**
* Checks <em>term equality</em> of two terms.
* This method returns a <code>boolean</code> whose value is
* (<code>this == obj</code>) if this variable is unbound.
* Otherwise, it returns the value of <code>val.equals(obj)</code>.
* @param obj the object to compare with. This must be dereferenced.
* @return <code>true</code> if this (or dereferenced term) is the same as the argument;
* <code>false</code> otherwise.
* @see #val
* @see #compareTo
*/
public boolean equals(Object obj) {
if(val != this)
return val.equals(obj);
if (! (obj instanceof VariableTerm)) // ???
return false; //???
return this == obj;
}
/**
* Returns a string representation of this term if unbound.
* Otherwise, returns the value of dereferenced term:
* <code>val.toString()</code>
* @see #val
*/
public String toString() {
if (val != this)
return val.toString();
return variableName();
}
/* Undoable */
public void undo() { val = this; }
/* Comparable */
/**
* Compares two terms in <em>Prolog standard order of terms</em>.<br>
* It is noted that <code>t1.compareTo(t2) == 0</code> has the same
* <code>boolean</code> value as <code>t1.equals(t2)</code>.
* @param anotherTerm the term to compare with. It must be dereferenced.
* @return the value <code>0</code> if two terms are identical;
* a value less than <code>0</code> if this term is <em>before</em> the <code>anotherTerm</code>;
* and a value greater than <code>0</code> if this term is <em>after</em> the <code>anotherTerm</code>.
*/
public int compareTo(Term anotherTerm) { // anotherTerm must be dereferenced.
if(val != this)
return val.compareTo(anotherTerm);
if (! anotherTerm.isVariable())
return BEFORE;
if (this == anotherTerm)
return EQUAL;
int x = this.hashCode() - ((VariableTerm)anotherTerm).hashCode();
if (x != 0)
return x;
throw new InternalException("VariableTerm is not unique");
}
}