package typing;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.tree.TreeNode;
import expressions.Expression;
import expressions.Let;
/**
* A node in the proof tree represented by {@link ProofTree}.
* Each node has an associated {@link typing.Judgement}.
*
* @author Benedikt Meurer
* @version $Id$
*/
public final class ProofNode implements TreeNode {
/**
* Convenience wrapper for {@link #ProofNode(Judgement, Rule)},
* which passes <code>null</code> for <code>rule</code>.
*
* @param judgement the type judgement for this proof node.
*/
ProofNode(Judgement judgement) {
this(judgement, null);
}
/**
* Allocates a new <code>ProofNode</code> instance with the
* given <code>judgement</code>.
*
* @param judgement the type judgement for this proof node.
* @param rule the type {@link Rule} that was applied for
* the newly allocated <code>ProofNode</code>
* or <code>null</code>.
*/
ProofNode(Judgement judgement, Rule rule) {
this.judgement = judgement;
this.rule = rule;
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#getChildAt(int)
*/
public ProofNode getChildAt(int childIndex) {
return this.children.elementAt(childIndex);
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#getChildCount()
*/
public int getChildCount() {
return this.children.size();
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#getParent()
*/
public ProofNode getParent() {
return this.parent;
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode)
*/
public int getIndex(TreeNode node) {
return this.children.indexOf(node);
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#getAllowsChildren()
*/
public boolean getAllowsChildren() {
return !isLeaf();
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#isLeaf()
*/
public boolean isLeaf() {
return (getChildCount() == 0);
}
/**
* {@inheritDoc}
*
* @see javax.swing.tree.TreeNode#children()
*/
public Enumeration children() {
return this.children.elements();
}
/**
* Returns <code>true</code> if this node and all
* sub nodes are finished. A node is finished if
* a {@link Rule} was applied and thereby a proper
* type is known.
*
* @return <code>true</code> if finished.
*/
public boolean isFinished() {
// check if this node has a rule
if (this.rule == null)
return false;
// check all children
for (ProofNode node : this.children)
if (!node.isFinished())
return false;
return true;
}
/**
* Returns the judgement associated with this
* proof node in the proof tree.
*
* @return Returns the judgement.
*/
public Judgement getJudgement() {
return this.judgement;
}
/**
* Returns the {@link Rule} that was applied for this
* proof node, or <code>null</code> if no rule was
* applied yet.
*
* @return the type rule that was applied or
* <code>null</code>.
*/
public Rule getRule() {
return this.rule;
}
/**
* Adds <code>node</code> as child node to this
* proof node. The parent of <code>node</code> will
* be set to point to this {@link ProofNode}.
*
* @param node the node to add to the children.
*
* @see #children()
* @see #getParent()
*/
void addChild(ProofNode node) {
this.children.add(node);
node.parent = this;
}
/**
* Convenience wrapper for the {@link #addChild(ProofNode)}
* method.
*
* @param judgement a {@link Judgement} to add.
*
* @see #addChild(ProofNode)
*/
void addChild(Judgement judgement) {
addChild(new ProofNode(judgement));
}
/**
* Returns <code>true</code> if this proof node or any of
* its sub nodes contains the given <code>node</code> in
* the list of children.
*
* @param node the child node to look up.
*
* @return <code>true</code> if <code>node</code> is a
* child of this proof node.
*/
boolean containsChild(ProofNode node) {
for (ProofNode child : this.children)
if (child == node || child.containsChild(node))
return true;
return false;
}
/**
* Returns <code>true</code> if this proof node or any of
* its sub nodes contains a type, which in turn contains
* a type variable of the given <code>name</code>.
*
* @param name the name of the type variable to test.
*
* @return <code>true</code> if a type variable of the
* given <code>name</code> is present for this
* node or any of its subnodes.
*/
boolean containsTypeVariable(String name) {
if (this.judgement.getType().containsFreeTypeVariable(name))
return true;
for (ProofNode child : this.children)
if (child.containsTypeVariable(name))
return true;
return false;
}
/**
* Clones this node and all subnodes. Replaces all occurances of
* <code>oldNode</code> in the current tree below this node with
* <code>newNode</code> in the cloned tree.
*
* @param oldNode the old node in the current tree.
* @param newNode the new node for <code>oldNode</code> in the
* cloned tree.
*
* @return the resulting tree starting at this node.
*/
ProofNode cloneSubstituteAndReplace(Substitution substitution, ProofNode oldNode, ProofNode newNode, TypeVariableAllocator typeVariableAllocator) {
// check if this one should be replaced
if (oldNode == this)
return newNode.cloneSubstituteAndReplace(substitution, null, null, typeVariableAllocator);
// allocate a new copy of the node
ProofNode node = new ProofNode(this.judgement.substitute(substitution, typeVariableAllocator), this.rule);
node.children = new Vector<ProofNode>();
// clone/replace all children
for (ProofNode oldChild : this.children) {
ProofNode newChild = oldChild.cloneSubstituteAndReplace(substitution, oldNode, newNode, typeVariableAllocator);
node.children.add(newChild);
newChild.parent = node;
}
// special case (P-LET): We can only add the second child once the first
// subtree is finished.
if (node.rule == Rule.P_LET && node.children.size() == 1 && node.isFinished()) {
Environment environment = node.judgement.getEnvironment();
Let let = (Let)node.judgement.getExpression();
MonoType tau = node.judgement.getType();
MonoType tau1 = node.children.firstElement().judgement.getType();
node.addChild(new Judgement(environment.extend(let.getId(), environment.closure(tau1)), let.getE2(), tau));
}
// and return the cloned node
return node;
}
ProofNode findNodeByExpression(Expression expression) {
if (this.judgement.getExpression() == expression) {
return this;
}
else {
for (ProofNode child : this.children) {
ProofNode node = child.findNodeByExpression(expression);
if (node != null)
return node;
}
}
return null;
}
// member attributes
private Judgement judgement;
private Rule rule;
private ProofNode parent;
private Vector<ProofNode> children = new Vector<ProofNode>();
}