package de.unisiegen.tpml.core ; import java.util.Enumeration ; import java.util.NoSuchElementException ; import java.util.Vector ; import javax.swing.tree.TreeNode ; /** * Abstract base class for proof nodes that present the fundamental parts of the * {@link de.unisiegen.tpml.core.AbstractProofModel}s. The user interface and * other modules should never access methods of this class directly, as they are * subject to change, instead the upper layer should restrict usage to the * {@link de.unisiegen.tpml.core.ProofNode} interface. * * @author Benedikt Meurer * @version $Rev$ * @see ProofNode * @see javax.swing.tree.TreeNode */ public abstract class AbstractProofNode implements ProofNode { // // Constants // /** * Empty {@link ProofRule} array which is returned from {@link #getRules()} * when no steps have been added to a proof node. */ private static final ProofRule [ ] EMPTY_ARRAY = new ProofRule [ 0 ] ; /** * The identifier counter. * Needed to give an unique id to every proof node. */ private static int idCounter = 1; /** * The unique id of this proof node */ private int id; /** * An enumeration that is always empty. This is used when an enumeration of a * leaf node's children is requested. */ @ SuppressWarnings ( "unchecked" ) protected static final Enumeration EMPTY_ENUMERATION = new Enumeration ( ) { public boolean hasMoreElements ( ) { return false ; } public Object nextElement ( ) { throw new NoSuchElementException ( "No more elements" ) ; //$NON-NLS-1$ } } ; // // Attributes // /** * The child nodes of this node, or <code>null</code> if this node has no * children. * * @see #children() * @see #getChildAt(int) * @see #getChildCount() */ protected Vector < AbstractProofNode > children ; /** * The parent node of this node, or <code>null</code> if this is the root * node of a proof tree. * * @see #getParent() * @see #setParent(AbstractProofNode) */ protected AbstractProofNode parent ; /** * The rules that have been applied to this proof node so far, or * <code>null</code> if no rules have been applied yet. * * @see #getRules() * @see #setRules(ProofRule[]) */ private ProofRule [ ] rules ; /** * The user object associated with this {@link ProofNode}. * * @see #getUserObject() * @see #setUserObject(Object) */ private transient Object userObject ; // // Constructor // /** * Allocates a new {@link AbstractProofNode} without any children and no link * to a parent. */ protected AbstractProofNode ( ) { this.rules = EMPTY_ARRAY ; this.id = AbstractProofNode.idCounter++; } // // Primitives // /** * {@inheritDoc} * * @see de.unisiegen.tpml.core.ProofNode#getRules() */ public ProofRule [ ] getRules ( ) { return this.rules ; } /** * Sets the {@link ProofRule}s that have been applied to this node so far to * the specified <code>rules</code>. If <code>rules</code> is * <code>null</code> it is assumed that no rules have been applied yet. * * @param pRules the new proof rules for this node. */ public void setRules ( ProofRule [ ] pRules ) { this.rules = ( pRules != null ) ? pRules : EMPTY_ARRAY ; } /** * Creates and returns a forward-order enumeration of this node's children. * Modifying this node's child array invalidates any child enumerations * created before the modification. * * @return an {@link Enumeration} of this node's children * @see javax.swing.tree.TreeNode#children() */ @ SuppressWarnings ( "unchecked" ) public Enumeration children ( ) { if ( this.children == null ) { return EMPTY_ENUMERATION ; } return this.children.elements ( ) ; } /** * {@inheritDoc} * * @see ProofNode#getChildAt(int) */ public AbstractProofNode getChildAt ( int childIndex ) { if ( this.children == null ) { throw new ArrayIndexOutOfBoundsException ( "node has no children" ) ; //$NON-NLS-1$ } return this.children.elementAt ( childIndex ) ; } /** * Returns the number of children of this node. * * @return an integer giving the number of children of this node. * @see javax.swing.tree.TreeNode#getChildCount() */ public int getChildCount ( ) { if ( this.children == null ) { return 0 ; } return this.children.size ( ) ; } /** * {@inheritDoc} * * @see ProofNode#getParent() */ public AbstractProofNode getParent ( ) { return this.parent ; } /** * Sets this node's parent to <code>parent</code> but does not change the * <code>parent</code>'s child array. This method is called from the * {@link #insert(AbstractProofNode, int)} and * {@link #remove(AbstractProofNode)} methods toreassign a child's parent, it * should not be messaged from anywhere else. * * @param pParent this node's new parent. */ public void setParent ( AbstractProofNode pParent ) { this.parent = pParent ; } /** * Returns the index of the specified child in this node's child array. If the * specified node is not a child of this node, returns <code>-1</code>. * This method performs a linear search and is O(n) where n is the number of * children. * * @param aChild the {@link TreeNode} to search for among this node's * children. * @return an integer giving the index of the node in this node's child array, * or <code>-1</code> if the specified node is a not a child of this * node. * @throws IllegalArgumentException if <code>aChild</code> is null. * @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode) */ public int getIndex ( TreeNode aChild ) { if ( aChild == null ) { throw new IllegalArgumentException ( "argument is null" ) ; //$NON-NLS-1$ } if ( ! isNodeChild ( aChild ) ) { return - 1 ; } return this.children.indexOf ( aChild ) ; } /** * Returns <code>true</code> if this node is allowed to have children. * * @return <code>true</code> if this node allows children, else * <code>false</code>. * @see javax.swing.tree.TreeNode#getAllowsChildren() */ public boolean getAllowsChildren ( ) { return true ; } // // User Objects // /** * {@inheritDoc} * * @see ProofNode#getUserObject() */ public Object getUserObject ( ) { return this.userObject ; } /** * {@inheritDoc} * * @see ProofNode#setUserObject(java.lang.Object) */ public void setUserObject ( Object pUserObject ) { this.userObject = pUserObject ; } // // Tree Queries // /** * {@inheritDoc} * * @see ProofNode#isNodeAncestor(TreeNode) */ public boolean isNodeAncestor ( TreeNode anotherNode ) { if ( anotherNode == null ) { return false ; } TreeNode ancestor = this ; do { if ( ancestor == anotherNode ) { return true ; } } while ( ( ancestor = ancestor.getParent ( ) ) != null ) ; return false ; } /** * {@inheritDoc} * * @see ProofNode#isNodeDescendant(ProofNode) */ public boolean isNodeDescendant ( ProofNode anotherNode ) { if ( anotherNode == null ) return false ; return anotherNode.isNodeAncestor ( this ) ; } /** * {@inheritDoc} * * @see ProofNode#isNodeRelated(ProofNode) */ public boolean isNodeRelated ( ProofNode aNode ) { return ( aNode != null ) && ( getRoot ( ) == aNode.getRoot ( ) ) ; } /** * {@inheritDoc} * * @see ProofNode#getRoot() */ public AbstractProofNode getRoot ( ) { AbstractProofNode ancestor = this ; AbstractProofNode previous ; do { previous = ancestor ; ancestor = ancestor.getParent ( ) ; } while ( ancestor != null ) ; return previous ; } /** * {@inheritDoc} * * @see ProofNode#isRoot() */ public boolean isRoot ( ) { return ( getParent ( ) == null ) ; } /** * {@inheritDoc} * * @see PostorderEnumeration * @see ProofNode#postorderEnumeration() */ public Enumeration < ProofNode > postorderEnumeration ( ) { return new PostorderEnumeration ( this ) ; } // // Child Queries // /** * {@inheritDoc} * * @see ProofNode#isNodeChild(TreeNode) */ public boolean isNodeChild ( TreeNode aNode ) { boolean retval ; if ( aNode == null ) { retval = false ; } else { if ( getChildCount ( ) == 0 ) { retval = false ; } else { retval = ( aNode.getParent ( ) == this ) ; } } return retval ; } /** * {@inheritDoc} * * @see ProofNode#getFirstChild() */ public AbstractProofNode getFirstChild ( ) { if ( getChildCount ( ) == 0 ) { throw new NoSuchElementException ( "node has no children" ) ; //$NON-NLS-1$ } return getChildAt ( 0 ) ; } /** * {@inheritDoc} * * @see ProofNode#getLastChild() */ public AbstractProofNode getLastChild ( ) { if ( getChildCount ( ) == 0 ) { throw new NoSuchElementException ( "node has no children" ) ; //$NON-NLS-1$ } return getChildAt ( getChildCount ( ) - 1 ) ; } /** * {@inheritDoc} * * @see ProofNode#getChildAfter(TreeNode) */ public AbstractProofNode getChildAfter ( TreeNode aChild ) { if ( aChild == null ) { throw new IllegalArgumentException ( "argument is null" ) ; //$NON-NLS-1$ } int index = getIndex ( aChild ) ; if ( index == - 1 ) { throw new IllegalArgumentException ( "node is not a child" ) ; //$NON-NLS-1$ } if ( index < getChildCount ( ) - 1 ) { return getChildAt ( index + 1 ) ; } return null ; } /** * {@inheritDoc} * * @see ProofNode#getChildBefore(TreeNode) */ public AbstractProofNode getChildBefore ( TreeNode aChild ) { if ( aChild == null ) { throw new IllegalArgumentException ( "argument is null" ) ; //$NON-NLS-1$ } int index = getIndex ( aChild ) ; if ( index == - 1 ) { throw new IllegalArgumentException ( "argument is not a child" ) ; //$NON-NLS-1$ } if ( index > 0 ) { return getChildAt ( index - 1 ) ; } return null ; } // // Leaf Queries // /** * Returns <code>true</code> if this node has no children. To distinguish * between nodes that have no children and nodes that <i>cannot</i> have * children (e.g. to distinguish files from empty directories), use this * method in conjunction with {@link #getAllowsChildren()}. * * @return <code>true</code> if this node has no children. * @see #getAllowsChildren() * @see javax.swing.tree.TreeNode#isLeaf() */ public boolean isLeaf ( ) { return ( getChildCount ( ) == 0 ) ; } /** * {@inheritDoc} * * @see ProofNode#getFirstLeaf() */ public AbstractProofNode getFirstLeaf ( ) { AbstractProofNode node = this ; while ( ! node.isLeaf ( ) ) { node = node.getFirstChild ( ) ; } return node ; } /** * {@inheritDoc} * * @see ProofNode#getLastLeaf() */ public AbstractProofNode getLastLeaf ( ) { AbstractProofNode node = this ; while ( ! node.isLeaf ( ) ) { node = node.getLastChild ( ) ; } return node ; } // // Insertion / Removal // /** * Removes <code>newChild</code> from its parent and makes it a child of * this node by adding it to the end of this node's child array. * * @param newChild node to add as a child of this node. * @throws IllegalArgumentException if <code>newChild</code> is * <code>null</code>. * @see #insert(AbstractProofNode, int) */ public void add ( AbstractProofNode newChild ) { if ( newChild != null && newChild.getParent ( ) == this ) { insert ( newChild , getChildCount ( ) - 1 ) ; } else { insert ( newChild , getChildCount ( ) ) ; } } /** * Removes <code>newChild</code> from its present parent (if it has a * parent), sets the child's parent to this node, and then adds the child to * this node's child array at index <code>childIndex</code>. * <code>newChild</code> must not be <code>null</code> and must not be an * ancestor of this node. * * @param newChild the {@link ProofNode} to insert under this node. * @param childIndex the index in this node's child array where this node is * to be inserted. * @throw ArrayIndexOutOfBoundsException if <code>childIndex</code> is out * of bounds. * @throws IllegalArgumentException if <code>newChild</code> is null or is * an ancestor of this node. * @see #isNodeDescendant(ProofNode) */ public void insert ( AbstractProofNode newChild , int childIndex ) { if ( newChild == null ) { throw new IllegalArgumentException ( "new child is null" ) ; //$NON-NLS-1$ } else if ( isNodeAncestor ( newChild ) ) { throw new IllegalArgumentException ( "new child is an ancestor" ) ; //$NON-NLS-1$ } // unlink from the old parent AbstractProofNode oldParent = newChild.getParent ( ) ; if ( oldParent != null ) { oldParent.remove ( newChild ) ; } // link to the new parent newChild.setParent ( this ) ; // allocate the list of children on demand if ( this.children == null ) { this.children = new Vector < AbstractProofNode > ( ) ; } // add to the list of children this.children.insertElementAt ( newChild , childIndex ) ; } /** * Removes the child at the specified index from this node's children and sets * that node's parent to <code>null</code>. The child node to remove must * be a <code>ProofNode</code>. * * @param childIndex the index in this node's child array of the child to * remove. * @throws ArrayIndexOutOfBoundsException if <code>childIndex</code> is out * of bounds. */ public void remove ( int childIndex ) { AbstractProofNode child = getChildAt ( childIndex ) ; this.children.removeElementAt ( childIndex ) ; child.setParent ( null ) ; } /** * Removes <code>aChild</code> from this node's child array, giving it a * <code>null</code> parent. * * @param aChild a child of this node to remove. * @throws IllegalArgumentException if <code>aChild</code> is * <code>null</code> or is not a child of this node. */ public void remove ( AbstractProofNode aChild ) { if ( aChild == null ) { throw new IllegalArgumentException ( "argument is null" ) ; //$NON-NLS-1$ } if ( ! isNodeChild ( aChild ) ) { throw new IllegalArgumentException ( "argument is not a child" ) ; //$NON-NLS-1$ } remove ( getIndex ( aChild ) ) ; } /** * Removes all of this node's children, setting their parents to * <code>null</code>. If this node has no children, this method does * nothing. */ public void removeAllChildren ( ) { for ( int i = getChildCount ( ) - 1 ; i >= 0 ; -- i ) { remove ( i ) ; } } /** * Removes the subtree rooted at this node from the tree, giving this node a * <code>null</code> parent. Does nothing if this node is the root of its * tree. */ public void removeFromParent ( ) { AbstractProofNode tmpParent = getParent ( ) ; if ( tmpParent != null ) { tmpParent.remove ( this ) ; } } // // Internal classes // /** * Implementation class for the * {@link AbstractProofNode#postorderEnumeration()} method, which enumerates * the nodes below a given abstract proof node in post-order. * * @see AbstractProofNode#postorderEnumeration() */ private static final class PostorderEnumeration implements Enumeration < ProofNode > { /** * The root. */ protected AbstractProofNode root ; /** * The children. */ @ SuppressWarnings ( "unchecked" ) protected Enumeration children ; /** * the sub tree. */ @ SuppressWarnings ( "unchecked" ) protected Enumeration subtree ; /** * Initializes the PostorderEnumeration. * * @param pRoot The root node. */ PostorderEnumeration ( AbstractProofNode pRoot ) { super ( ) ; this.root = pRoot ; this.children = pRoot.children ( ) ; this.subtree = EMPTY_ENUMERATION ; } /** * Returns true, if the enumeration has more elements. * * @return True, if the enumeration has more elements. * @see java.util.Enumeration#hasMoreElements() */ public boolean hasMoreElements ( ) { return ( this.root != null ) ; } /** * Returns the next AbstractProofNode. * * @return The next AbstractProofNode. * @see java.util.Enumeration#nextElement() */ public AbstractProofNode nextElement ( ) { AbstractProofNode node ; if ( this.subtree.hasMoreElements ( ) ) { node = ( AbstractProofNode ) this.subtree.nextElement ( ) ; } else if ( this.children.hasMoreElements ( ) ) { this.subtree = new PostorderEnumeration ( ( AbstractProofNode ) this.children.nextElement ( ) ) ; node = ( AbstractProofNode ) this.subtree.nextElement ( ) ; } else { node = this.root ; this.root = null ; } return node ; } } /** * * Get the unique id of this proof node * * @return int unique id */ public int getId ( ) { return this.id; } }