From: Peter Bernard West Date: Mon, 4 Nov 2002 15:17:10 +0000 (+0000) Subject: Node and TreeException externalised from Tree. X-Git-Tag: Alt-Design_pre_src-java-org~184 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=44f0d9f4ad8e473b2e01dfc37a31eb830027c759;p=xmlgraphics-fop.git Node and TreeException externalised from Tree. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/FOP_0-20-0_Alt-Design@195411 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/org/apache/fop/datastructs/Node.java b/src/org/apache/fop/datastructs/Node.java new file mode 100644 index 000000000..116437f3a --- /dev/null +++ b/src/org/apache/fop/datastructs/Node.java @@ -0,0 +1,959 @@ +/* + * $Id$ + * + * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. + * For details on use and redistribution please refer to the + * LICENSE file included with these sources. + * + */ + +package org.apache.fop.datastructs; + +import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.Iterator; +import java.util.ListIterator; + +/* + * @author Peter B. West + * @version $Revision$ $Name$ + */ + + +/** + * Class Node, with class Tree, provides the + * structure for a general-purpose tree.

+ *
+ * Node
+ * +-------------------------+
+ * |(Node) parent            |
+ * +-------------------------+
+ * |ArrayList                |
+ * |+-----------------------+|
+ * ||(Node) child 0         ||
+ * |+-----------------------+|
+ * |:                       :|
+ * |+-----------------------+|
+ * ||(Node) child n         ||
+ * |+-----------------------+|
+ * +-------------------------+
+ * 
+ *

ArrayList is used for the list of children because the + * synchronization is performed "manually" within the individual methods, + * + *

Note that there is no payload carried by the Node. This class must + * be subclassed to carry any actual node contents. + * + *

See Tree for the tree-wide support methods and fields.

+ */ + +public class Node implements Cloneable { + + private Tree tree; + private Node parent; + private ArrayList children; // ArrayList of Node + /** Creation size of the children ArrayList. */ + private static final int FAMILYSIZE = 4; + + /** + * No argument constructor. + * + * Assumes that this node is the root, and so will throw a + * TreeException when the root node in the enclosing + * Tree object is non-null. + */ + + public Node(Tree tree) + throws TreeException { + if (tree.getRoot() != null) { + throw new TreeException( + "No arg constructor invalid when root exists"); + } + this.tree = tree; + parent = null; + tree.setRoot(this); + //children = new ArrayList(); + } + + /** + * @param tree the Tree to which this Node belongs. + * @param parent Node which is the parent of this Node. if this is + * null, the generated Node is assumed to be the root + * node. If the Tree root node is already set, throws + * a TreeException. + * @param index int index of child in parent. + */ + + public Node(Tree tree, Node parent, int index) + throws TreeException, IndexOutOfBoundsException { + this.tree = tree; + //children = new ArrayList(); + if (parent == null) { + if (tree.root != null) { + throw new TreeException("Null Node constructor " + + "invalid when root exists"); + } + this.parent = null; + tree.setRoot(this); + } + else { + // The parent must be a node in the current tree + if (parent.getTree() != tree) { + throw new TreeException("Parent not in same tree"); + } + this.parent = parent; + // connect me to my parent + parent.addChild(index, this); + } + } + + /** + * @param tree the Tree to which this Node belongs. + * @param parent Node which is the parent of this Node. if this is + * null, the generated Node is assumed to be the root + * node. If the Tree root node is already set, throws + * a TreeException. + */ + + public Node(Tree tree, Node parent) + throws TreeException, IndexOutOfBoundsException { + this.tree = tree; + //children = new ArrayList(); + if (parent == null) { + if (tree.getRoot() != null) { + throw new TreeException("Null Node constructor " + + "invalid when root exists"); + } + this.parent = null; + tree.setRoot(this); + } + else { + // The parent must be a node in the current tree + if (parent.getTree() != tree) { + throw new TreeException("Parent not in same tree"); + } + this.parent = parent; + // connect me to my parent + parent.addChild(this); + } + } + + + /** + * Appends a child Node to this node. Synchronized on the + * containing Tree object. + * + * Calls the modified method of the containing Tree to + * maintain the value of modCount. + * + * @param child Node to be added. + */ + + public void addChild(Node child) { + synchronized (tree) { + if (children == null) + children = new ArrayList(FAMILYSIZE); + children.add((Object) child); + tree.modified(); + } + } + + /** + * Adds a child Node in this node at a specified index + * position. + * Synchronized on the containing Tree object. + * + * Calls the modified method of the containing Tree to + * maintain the value of modCount. + * + * @param index int position of new child + * @param child Node to be added. + */ + + public void addChild(int index, Node child) + throws IndexOutOfBoundsException { + synchronized (tree) { + if (children == null) + children = new ArrayList(FAMILYSIZE); + children.add(index, (Object) child); + tree.modified(); + } + } + + /** + * Copies a subtree of this tree as a new child of this node. + * Synchronized on the containing Tree object. + * + * Calls the modified method of the containing Tree to + * maintain the value of modCount. + * + * Note that it is illegal to try to copy a subtree to one of + * its own descendents or itself. (This restriction could be lifted + * by creating a new Tree containing the subtree, and defining an + * attachTree() method to attach one Tree to another.) + * + * This is the public entry to copyCheckSubTree. It will always + * perform a check for the attempt to copy onto a descendent or + * self. It calls copyCheckSubTree. + * + * @param subtree Node at the root of the subtree to be added. + * @param index int index of child position in Node's children + */ + + public void copySubTree(Node subtree, int index) + throws TreeException, ConcurrentModificationException { + copyCheckSubTree(subtree, index, true); + } + + /** + * Copies a subtree of this tree as a new child of this node. + * Synchronized on the containing Tree object. + * + * Calls the modified method of the containing Tree to + * maintain the value of modCount. + * + * Note that it is illegal to try to copy a subtree to one of + * its own descendents or itself. (This restriction could be lifted + * by creating a new Tree containing the subtree, and defining an + * attachTree() method to attach one Tree to another.) + * + * WARNING: this version of the method assumes that Node + * will be subclassed; Node has no contents, so for + * the tree to carry any data the Node must be subclassed. As a + * result, this method copies nodes by performing a clone() + * operation on the nodes being copied, rather than issuing a + * new Node(..) call. It then adjusts the necessary + * references to position the cloned node under the correct parent. + * As part of this process, the method must create a new empty + * children ArrayList. if this is not done, + * subsequent addChild() operations on the node will affect + * the original children array. + * + * This warning applies to the contents of any subclassed + * Node. All references in the copied subtree will be to + * the objects from the original subtree. If this has undesirable + * effects, the method must be overridden so that the copied subtree + * can have its references adjusted after the copy. + * + * @param subtree Node at the root of the subtree to be added. + * @param index int index of child position in Node's children + * @param checkLoops boolean - should the copy been checked for + * loops. Set this to true on the first + * call. + */ + + private void copyCheckSubTree( + Node subtree, int index, boolean checkLoops) + throws TreeException, ConcurrentModificationException { + synchronized (tree) { + Node newNode = null; + if (checkLoops) { + checkLoops = false; + if (subtree == this) { + throw new TreeException + ("Copying subtree onto itself."); + } + + // Check that subtree is not an ancestor of this. + Ancestor ancestors = + new Ancestor(tree.getModCount()); + while (ancestors.hasNext()) { + if ((Node)ancestors.next() == subtree) { + throw new TreeException + ("Copying subtree onto descendent."); + } + } + } + + // Clone (shallow copy) the head of the subtree + try { + newNode = (Node)subtree.clone(); + } catch (CloneNotSupportedException e) { + throw new TreeException( + "clone() not supported on Node"); + } + + // Attach the clone to this at the indicated child index + newNode.parent = this; + this.addChild(index, newNode); + if (newNode.numChildren() != 0) { + // Clear the children arrayList + newNode.children = new ArrayList(newNode.numChildren()); + // Now iterate over the children of the root of the + // subtree, adding a copy to the newly created Node + Iterator iterator = subtree.nodeChildren(); + while (iterator.hasNext()) { + newNode.copyCheckSubTree((Node)iterator.next(), + newNode.numChildren(), + checkLoops); + } + } + tree.modified(); + } + } + + + /** + * Removes the child Node at the specified index in the + * ArrayList. Synchronized on the enclosing Tree object. + * + * Calls the modified method of the containing Tree to + * maintain the value of modCount. + * + * @param index The int index of the child to be removed. + * @return the node removed. + */ + + public Node removeChildAtIndex(int index) { + synchronized (tree) { + Node tmpNode = (Node) children.remove(index); + tree.modified(); + return tmpNode; + } + } + + /** + * Removes the specified child Node from the children + * ArrayList. Synchronized on the enclosing Tree object. + * + * Implemented by calling removeChildAtIndex(). Relies + * on that method to call the modified method of the + * containing Tree to maintain the value of modCount. + * + * @param child The child node to be removed. + * @return the node removed. + */ + + public Node removeChild(Node child) + throws NoSuchElementException { + synchronized (tree) { + int index = children.indexOf((Object) child); + if (index == -1) { + throw new NoSuchElementException(); + } + Node tmpNode = removeChildAtIndex(index); + // Note - no call to tree.modified() here - + // done in removeChildAtindex() + return tmpNode; + } + } + + /** + * Deletes the entire subtree rooted on this from the + * Tree. The Tree is + * traversed in PostOrder, and each Node is removed in PostOrder. + * @return int count of Nodes deleted. + */ + public int deleteSubTree() { + synchronized (tree) { + int count = delete(this); + tree.modified(); + return count; + } + } + + /** + * N.B. this private method must only be called from the deleteSubTree + * method, which is synchronized. In itself, it is not synchronized. + * @param subtree Node at the root of the subtree to be deleted. + * @return int count of Nodes deleted. + */ + private int delete(Node subtree) { + int count = 0; + + while (subtree.numChildren() > 0) { + //System.out.println("# children "+subtree.numChildren()); + + count += delete((Node)subtree.children.get(0)); + } + // Delete this node + // nullify the parent reference + if (subtree.getTree().getRoot() != subtree) { + // Not the root node - remove from parent + subtree.getParent().removeChild(subtree); + subtree.unsetParent(); + } else { + subtree.getTree().unsetRoot(); + } // end of else + return ++count; + } + + public Tree getTree() { + return tree; + } + + public Node getParent() { + return (Node) parent; + } + + public void unsetParent() { + parent = null; + } + + public Node getChild(int index) { + return (Node) children.get(index); + } + + public Iterator nodeChildren() { + return children.iterator(); + } + + public int numChildren() { + return children.size(); + } + + /** + * Class PreOrder is a member class of Node. + * + * It implements the Iterator interface, excluding the + * optional remove method. The iterator traverses its + * containing Tree from its containing Node in + * preorder order. + * + * The method is implemented recursively; + * at each node, a PreOrder object is instantiated to handle the + * node itself and to trigger the handing of the node's children. + * The node is returned first, and then for each child, a new + * PreOrder is instantiated. That iterator will terminate when the + * last descendant of that child has been returned. + * + * The iterator is fast-fail. If any modifications occur to + * the tree as a whole during the lifetime of the iterator, a + * subsequent call to next() will throw a + * ConcurrentModificationException. See the discussion of + * fast-fail iterators in AbstractList. + * + * The modCount field used to maintain information about + * structural modifcations is maintained for all nodes in the + * containing Tree instance. + */ + + class PreOrder implements Iterator { + private boolean selfNotReturned = true; + private int nextChildIndex = 0; // N.B. this must be kept as + // the index of the active child until that child is exhausted. + // At the start of proceedings, it may already point past the + // end of the (empty) child vector + + private int age; + private PreOrder nextChildIterator; + + /** + * Constructor + * + * @param age the current value of the modCount field in the + * Tree instance which includes this class instance. + */ + public PreOrder(int age) { + this.age = age; + hasNext(); // A call to set up the initial iterators + // so that a call to next() without a preceding call to + // hasNext() will behave sanely + } + + public boolean hasNext() { + // synchronize this against possible changes to the tree + synchronized (tree) { + if (selfNotReturned) { + return true; + } + // self has been returned - are there any children? + // if so, we must always have an iterator available + // even unless it is exhausted. Assume it is set up this + // way by next(). The iterator has a chance to do this + // because self will always be returned first. + // The test of nextChildIndex must always be made because + // there may be no children, or the last child may be + // exhausted, hence no possibility of an + // iterator on the children of any child. + if (nextChildIndex < children.size()) { + return nextChildIterator.hasNext(); + } + else { // no kiddies + return false; + } + } + } + + public Object next() + throws NoSuchElementException, + ConcurrentModificationException { + synchronized (tree) { + // synchronize the whole against changes to the tree + + // Check for ConcurrentModification + if (! tree.modCountEqualTo(age)) { + throw new ConcurrentModificationException(); + } + + if (! hasNext()) { + throw new NoSuchElementException(); + } + if (selfNotReturned) { + selfNotReturned = false; + if (nextChildIndex < children.size()) { + // We have children - create an iterator + // for the first one + nextChildIterator = + ((Node) + (children.get(nextChildIndex))).new + PreOrder(age); + } + // else do nothing; + // the nextChildIndex test in hasNext() + // will prevent us from getting into trouble + return Node.this; + } + else { // self has been returned + // there must be a next available, or we would not have + // come this far + if (! nextChildIterator.hasNext()) { + // last iterator was exhausted; + // if there was another child available, an + //iterator would already have been set up. + // Every iterator will return at least one node - + // the node on which it is defined. + // So why did the initial hasNext succeed? + throw new NoSuchElementException( + "Cannot reach this"); + } + Object tempNode = nextChildIterator.next(); + // Check for exhaustion of the child + if (! nextChildIterator.hasNext()) { + // child iterator empty - another child? + if (++nextChildIndex < children.size()) { + nextChildIterator = + ((Node) + (children.get(nextChildIndex))).new + PreOrder(age); + } + else { + // nullify the iterator + nextChildIterator = null; + } + } + return (Node) tempNode; + } + } + } + + public void remove() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + } + + + /** + * Class PostOrder is a member class of Node. + * + * It implements the Iterator interface, excluding the + * optional remove method. The iterator traverses its + * containing Tree from its containing Node in + * postorder order. + * + * The method is implemented recursively; + * at each node, a PostOrder object is instantiated to handle the + * node itself and to trigger the handing of the node's children. + * Firstly, for each child a new PostOrder is instantiated. + * That iterator will terminate when the last descendant of that + * child has been returned. finally, the node itself is returned. + * + * The iterator is fast-fail. iI any modifications occur to + * the tree as a whole during the lifetime of the iterator, a + * subsequent call to next() will throw a + * ConcurrentModificationException. See the discussion of + * fast-fail iterators in AbstractList. + * + * The modCount field used to maintain information about + * structural modifcations is maintained for all nodes in the + * containing Tree instance. + */ + + class PostOrder implements Iterator { + private boolean selfReturned = false; + private int nextChildIndex = 0; // N.B. this must be kept as + // the index of the active child until that child is exhausted. + // At the start of proceedings, it may already point past the + // end of the (empty) child vector + + private int age; + private PostOrder nextChildIterator; + + /** + * Constructor + * + * @param age the current value of the modCount field in the + * Tree instance which includes this class instance. + */ + public PostOrder(int age) { + this.age = age; + hasNext(); // A call to set up the initial iterators + // so that a call to next() without a preceding call to + // hasNext() will behave sanely + } + + public boolean hasNext() { + // Synchronize this against changes in the tree + synchronized (tree) { + // self is always the last to go + if (selfReturned) { // nothing left + return false; + } + + // Check first for children, and set up an iterator if so + if (nextChildIndex < children.size()) { + if (nextChildIterator == null) { + nextChildIterator = + ((Node) + (children.get(nextChildIndex))).new + PostOrder(age); + } + // else an iterator exists. + // Assume that the next() method + // will keep the iterator current + } // end of Any children? + + return true; + } + } + + public Object next() + throws NoSuchElementException, + ConcurrentModificationException { + synchronized (tree) { + // synchronize the whole against changes to the tree + + // Check for ConcurrentModification + if (! tree.modCountEqualTo(age)) { + throw new ConcurrentModificationException(); + } + + if (! hasNext()) { + throw new NoSuchElementException(); + } + // Are there any children? + if (nextChildIndex < children.size()) { + // There will be an active iterator. Is it empty? + if (nextChildIterator.hasNext()) { + // children remain + Object tempNode = nextChildIterator.next(); + // now check for exhaustion of the iterator + if (! nextChildIterator.hasNext()) { + if (++nextChildIndex < children.size()) { + nextChildIterator = + ((Node) + (children.get(nextChildIndex))).new + PostOrder(age); + } + // else leave the iterator bumping on empty + // next call will return self + } + // else iterator not exhausted + // return the Node + return (Node) tempNode; + } + // else children exhausted - fall through + } + // No children - return self object + selfReturned = true; + nextChildIterator = null; + return Node.this; + } + } + + public void remove() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + } + + + /** + * Class Ancestor is a member class of Node. + * + * It implements the Iterator interface, excluding the + * optional remove method. The iterator traverses the + * ancestors of its containing Node from the Node's immediate + * parent back to tge root Node of the containing Tree. + * + * The iterator is fast-fail. If any modifications occur to + * the tree as a whole during the lifetime of the iterator, a + * subsequent call to next() will throw a + * ConcurrentModificationException. See the discussion of + * fast-fail iterators in AbstractList. + * + * The modCount field used to maintain information about + * structural modifcations is maintained for all nodes in the + * containing Tree instance. + */ + + class Ancestor implements Iterator { + private Node nextAncestor; + private int age; + + /** + * Constructor + * + * @param age the current value of the modCount field in the + * Tree instance which includes this class instance. + */ + + public Ancestor(int age) { + this.age = age; + nextAncestor = Node.this.parent; + } + + public boolean hasNext() { + return nextAncestor != null; + } + + public Object next() + throws NoSuchElementException, + ConcurrentModificationException { + synchronized (tree) { + // The tree is a + // potentially dymanic structure, which could be + // undergoing modification as this method is being + // executed, and it is possible that the Comod exception + // could be set to trigger while this call is in process. + if (! tree.modCountEqualTo(age)) { + throw new ConcurrentModificationException(); + } + Node tmpNode = nextAncestor; + nextAncestor = tmpNode.parent; + return tmpNode; + } + } + + public void remove() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + } + + + /** + * Class FollowingSibling is a member class of Node. + * + * It implements the ListIterator interface, but has reports + * UnsupportedOperationException for all methods except + * hasNext(), next() and nextIndex(). + * These methods are implemented as synchronized wrappers around the + * underlying ArrayList methods. + * + * The listIterator traverses those children in the parent node's + * children ArrayList which follow the subject + * node's entry in that array, using the next() method. + * + * The iterator is fail-fast. if any modification occur to + * the tree as a whole during the lifetime of the iterator, a + * subsequent call to next() will throw a + * ConcurrentModificationException. See the discussion of + * fast-fail iterators in AbstractList. + * + * The fail-fast ListIterator in ArrayList is the underlying + * mechanism for both the listIterator and the fail-fast + * behaviour. + */ + + class FollowingSibling implements ListIterator { + + private ListIterator listIterator; + private ArrayList rootDummy = new ArrayList(); + // An empty ArrayList for the root listIterator + // hasNext() will always return false + + public FollowingSibling() { + synchronized (tree) { + // Set up iterator on the parent's arrayList of children + Node refNode = Node.this.parent; + if (refNode != null) { + // Not the root node; siblings may exist + // Set up iterator on the parent's children ArrayList + ArrayList siblings = refNode.children; + int index = siblings.indexOf((Object) Node.this); + // if this is invalid, we are in serious trouble + listIterator = siblings.listIterator(index + 1); + } // end of if (Node.this.parent != null) + else { + // Root node - no siblings + listIterator = rootDummy.listIterator(); + } + } + } + + public boolean hasNext() { + // Any CoMod exception will be thrown by the listIterator + // provided with ArrayList. It does not throw such exceptions + // on calls to hasNext(); + synchronized (tree) { + return listIterator.hasNext(); + } + } + + public Object next() + throws NoSuchElementException, + ConcurrentModificationException { + synchronized (tree) { + // N.B. synchronization here is still on the Tree + // rather than on the Node containing the children + // ArryList of interest. Other ArrayList operations + // throughout the Tree are synchronized on the Tree object + // itself, so exceptions cannot be made for these more + // directly Nodal operations. + return listIterator.next(); + } + } + + public int nextIndex() { + synchronized (tree) { + return listIterator.nextIndex(); + } + } + + public void add(Object o) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public void remove() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public boolean hasPrevious() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public Object previous() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public int previousIndex() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public void set(Object o) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + } + + /** + * Class PrecedingSibling is a member class of Node. + * + * It implements the ListIterator interface, but has reports + * UnsupportedOperationException for all methods except + * hasPrevious(), previous() and + * previousIndex(). + * These methods are implemented as synchronized wrappers around the + * underlying ArrayList methods. + * + * The listIterator traverses those children in the parent node's + * children ArrayList which precede the subject + * node's entry in that array, using the previous() method. + * I.e., siblings are produced in reverse sibling order. + * + * The iterator is fail-fast. if any modification occur to + * the tree as a whole during the lifetime of the iterator, a + * subsequent call to previous() will throw a + * ConcurrentModificationException. See the discussion of + * fast-fail iterators in AbstractList. + * + * The fail-fast ListIterator in ArrayList is the underlying + * mechanism for both the listIterator and the fail-fast + * behaviour. + */ + + class PrecedingSibling implements ListIterator { + + private ListIterator listIterator; + private ArrayList rootDummy = new ArrayList(); + // An empty ArrayList for the root listIterator + // hasNext() will always return false + + public PrecedingSibling() { + synchronized (tree) { + // Set up iterator on the parent's arrayList of children + Node refNode = Node.this.parent; + if (refNode != null) { + // Not the root node; siblings may exist + // Set up iterator on the parent's children ArrayList + ArrayList siblings = refNode.children; + int index = siblings.indexOf((Object) Node.this); + // if this is invalid, we are in serious trouble + listIterator = siblings.listIterator(index); + } // end of if (Node.this.parent != null) + else { + // Root node - no siblings + listIterator = rootDummy.listIterator(); + } + } + } + + public boolean hasPrevious() { + // Any CoMod exception will be thrown by the listIterator + // provided with ArrayList. It does not throw such exceptions + // on calls to hasNext(); + synchronized (tree) { + return listIterator.hasPrevious(); + } + } + + public Object previous() + throws NoSuchElementException, + ConcurrentModificationException { + synchronized (tree) { + // N.B. synchronization here is still on the Tree + // rather than on the Node containing the children + // ArryList of interest. Other ArrayList operations + // throughout the Tree are synchronized on the Tree object + // itself, so exceptions cannot be made for these more + // directly Nodal operations. + return listIterator.previous(); + } + } + + public int previousIndex() { + synchronized (tree) { + return listIterator.previousIndex(); + } + } + + public void add(Object o) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public void remove() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public boolean hasNext() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public Object next() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public int nextIndex() + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public void set(Object o) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/org/apache/fop/datastructs/Tree.java b/src/org/apache/fop/datastructs/Tree.java index e24a4921a..89b19e6d8 100644 --- a/src/org/apache/fop/datastructs/Tree.java +++ b/src/org/apache/fop/datastructs/Tree.java @@ -9,40 +9,18 @@ package org.apache.fop.datastructs; -import java.util.ArrayList; -import java.util.ConcurrentModificationException; -import java.util.NoSuchElementException; -import java.util.Iterator; -import java.util.ListIterator; - -// TODO: -// Should I provide a separate or supplementary exception for CoMods appends -// on the trailing edge of the tree? I.e., for each append, check if it is an -// append to a node which is the final sibling at any level of the tree. -// If so, set a copy the modCount value as the trailingEdgeModAge. -// If a ConcurrentModificationException is about to be thrown, check whether -// the trailingEdgeModAge is the same as the modCount. If so, throw a -// ConcurrentTreeAppendException instead. Probably, make that a subclass of -// ConcurrentModificationException so that the check can be done on the -// catch of the CoModEx. /** - * A generalised tree class with traversal Iterators. + * A generalised tree class. * *

The Tree class is analogous to one of the Collection * classes. It provides a bag with a certain structure into which objects - * may be collected for manipulation.

+ * may be collected for manipulation. * *

The outer class, Tree, is the level at which are defined those fields * and methods which are provided for the manipulation of the tree as a * whole. The tree is actually comprised of a collection of Node - * elements.

- * - *

The primary reasons for the existence of a separate Tree - * class is to provide an object for tree-wide synchronization, and to - * have a home for modCount for the provision of - * fast-fail iterators. For more details, see the - * discussion of modCount in AbstractList.

+ * elements. * * @author Peter B. West * @version $Revision$ $Name$ @@ -56,9 +34,16 @@ public class Tree { * AbstractList. */ protected int modCount = 0; + + /** + * Count of the nodes in this tree. + */ protected int nodeCount = 0; - protected Node root; + /** + * The root node of this tree. + */ + protected Node root = null; public Tree() {} @@ -71,970 +56,66 @@ public class Tree { } } + /** + * Get the value of the modCount field, used to warn of concurrent + * modification of the tree during certain unsynchronized operations. + * @return - the int modCount. + */ public int getModCount() { synchronized (this) { return modCount; } } + /** + * Test the modCount field value. + * @param value - the value to test against modCount. + * @return boolean test result. + */ public boolean modCountEqualTo(int value) { synchronized (this) { return value == modCount; } } + /** + * Get the number of nodes in the tree. + * @return the number of nodes. + */ public int size() { return nodeCount; } + /** + * Is the tree empty? + * @return boolean answer to the question. Tests whether the + * root node is null. + */ public boolean isEmpty() { - return nodeCount == 0; + return root == null; } - public Node getRoot() { - return root; - } - - public void unsetRoot() { - root = null; + /** + * Set the root field. + * @param root the Node which is to be the root of the tree. + */ + public void setRoot(Node root) { + this.root = root; } - - public class TreeException extends Exception { - public TreeException(String message) { - super(message); - } - + /** + * Get the root node of the tree. + * @return the root Node. + */ + public Node getRoot() { + return root; } /** - *

Member class Node of class Tree provides the - * structure for a general-purpose tree.

- *
-     * Node
-     * +-------------------------+
-     * |(Node) parent            |
-     * +-------------------------+
-     * |ArrayList                |
-     * |+-----------------------+|
-     * ||(Node) child 0         ||
-     * |+-----------------------+|
-     * |:                       :|
-     * |+-----------------------+|
-     * ||(Node) child n         ||
-     * |+-----------------------+|
-     * +-------------------------+
-     * 
- *

ArrayList is used for the list of children because the - * synchronization is performed "manually" within the individual methods, - * and beause the fail-fast characteristics of the ArrayList - * iterator and listIterators is desired.

- * - *

Note that there is no payload carried by the Node. This class must - * be subclassed to carry any actual node contents.

- * - *

See Tree for the tree-wide support methods and fields.

+ * Clear the root field. I.e., empty the tree. */ - - public class Node implements Cloneable { - - private Node parent; - private ArrayList children; // ArrayList of Node - //protected Object content; - - /** - * No argument constructor. - * - * Assumes that this node is the root, and so will throw a - * TreeException when the root node in the enclosing - * Tree object is non-null. - */ - - public Node() - throws TreeException { - if (Tree.this.root != null) { - throw new TreeException( - "No arg constructor invalid when root exists"); - } - parent = null; - Tree.this.root = this; - children = new ArrayList(); - //content = null; - } - - /** - * @param parent Node which is the parent of this Node. if this is - * null, the generated Node is assumed to be the root - * node. If the Tree root node is already set, throws - * a TreeException. - * @param index int index of child in parent. - */ - - public Node(Node parent, int index) - throws TreeException, IndexOutOfBoundsException { - children = new ArrayList(); - //content = null; - if (parent == null) { - if (Tree.this.root != null) { - throw new TreeException("Null Node constructor " - + "invalid when root exists"); - } - this.parent = null; - Tree.this.root = this; - } - else { - // The parent must be a node in the current tree - if (parent.getTree() != Tree.this) { - throw new TreeException("Parent not in same tree"); - } - this.parent = parent; - // connect me to my parent - parent.addChild(index, this); - } - } - - /** - * @param parent Node which is the parent of this Node. if this is - * null, the generated Node is assumed to be the root - * node. If the Tree root node is already set, throws - * a TreeException. - */ - - public Node(Node parent) - throws TreeException, IndexOutOfBoundsException { - children = new ArrayList(); - //content = null; - if (parent == null) { - if (Tree.this.root != null) { - throw new TreeException("Null Node constructor " - + "invalid when root exists"); - } - this.parent = null; - Tree.this.root = this; - } - else { - // The parent must be a node in the current tree - if (parent.getTree() != Tree.this) { - throw new TreeException("Parent not in same tree"); - } - this.parent = parent; - // connect me to my parent - parent.addChild(this); - } - } - - - /** - * Appends a child Node to this node. Synchronized on the - * containing Tree object. - * - * Calls the modified method of the containing Tree to - * maintain the value of modCount. - * - * @param child Node to be added. - */ - - public void addChild(Node child) { - synchronized (Tree.this) { - children.add((Object) child); - Tree.this.modified(); - } - } - - /** - * Adds a child Node in this node at a specified index - * position. - * Synchronized on the containing Tree object. - * - * Calls the modified method of the containing Tree to - * maintain the value of modCount. - * - * @param index int position of new child - * @param child Node to be added. - */ - - public void addChild(int index, Node child) - throws IndexOutOfBoundsException { - synchronized (Tree.this) { - children.add(index, (Object) child); - Tree.this.modified(); - } - } - - /** - * Copies a subtree of this tree as a new child of this node. - * Synchronized on the containing Tree object. - * - * Calls the modified method of the containing Tree to - * maintain the value of modCount. - * - * Note that it is illegal to try to copy a subtree to one of - * its own descendents or itself. (This restriction could be lifted - * by creating a new Tree containing the subtree, and defining an - * attachTree() method to attach one Tree to another.) - * - * This is the public entry to copyCheckSubTree. It will always - * perform a check for the attempt to copy onto a descendent or - * self. It calls copyCheckSubTree. - * - * @param subtree Node at the root of the subtree to be added. - * @param index int index of child position in Node's children - */ - - public void copySubTree(Node subtree, int index) - throws TreeException, ConcurrentModificationException { - copyCheckSubTree(subtree, index, true); - } - - /** - * Copies a subtree of this tree as a new child of this node. - * Synchronized on the containing Tree object. - * - * Calls the modified method of the containing Tree to - * maintain the value of modCount. - * - * Note that it is illegal to try to copy a subtree to one of - * its own descendents or itself. (This restriction could be lifted - * by creating a new Tree containing the subtree, and defining an - * attachTree() method to attach one Tree to another.) - * - * WARNING: this version of the method assumes that Tree.Node - * will be subclassed; Tree.Node has no contents, so for - * the tree to carry any data the Node must be subclassed. As a - * result, this method copies nodes by performing a clone() - * operation on the nodes being copied, rather than issuing a - * new Tree.Node(..) call. It then adjusts the necessary - * references to position the cloned node under the correct parent. - * As part of this process, the method must create a new empty - * children ArrayList. if this is not done, - * subsequent addChild() operations on the node will affect - * the original children array. - * - * This warning applies to the contents of any subclassed - * Tree.Node. All references in the copied subtree will be to - * the objects from the original subtree. If this has undesirable - * effects, the method must be overridden so that the copied subtree - * can have its references adjusted after the copy. - * - * @param subtree Node at the root of the subtree to be added. - * @param index int index of child position in Node's children - * @param checkLoops boolean - should the copy been checked for - * loops. Set this to true on the first - * call. - */ - - private void copyCheckSubTree( - Node subtree, int index, boolean checkLoops) - throws TreeException, ConcurrentModificationException { - synchronized (Tree.this) { - Node newNode = null; - if (checkLoops) { - checkLoops = false; - if (subtree == this) { - throw new TreeException - ("Copying subtree onto itself."); - } - - // Check that subtree is not an ancestor of this. - Ancestor ancestors = - new Ancestor(Tree.this.getModCount()); - while (ancestors.hasNext()) { - if ((Node)ancestors.next() == subtree) { - throw new TreeException - ("Copying subtree onto descendent."); - } - } - } - - //Node newNode = new Node(this, index, subtree.getContent()); - // Clone (shallow copy) the head of the subtree - try { - newNode = (Node)subtree.clone(); - } catch (CloneNotSupportedException e) { - throw new TreeException( - "clone() not supported on Tree.Node"); - } - - // Attach the clone to this at the indicated child index - newNode.parent = this; - this.addChild(index, newNode); - // Clear the children arrayList - newNode.children = new ArrayList(newNode.numChildren()); - // Now iterate over the children of the root of the - // subtree, adding a copy to the newly created Node - Iterator iterator = subtree.nodeChildren(); - while (iterator.hasNext()) { - newNode.copyCheckSubTree((Node)iterator.next(), - newNode.numChildren(), - checkLoops); - } - Tree.this.modified(); - } - } - - - /** - * Removes the child Node at the specified index in the - * ArrayList. Synchronized on the enclosing Tree object. - * - * Calls the modified method of the containing Tree to - * maintain the value of modCount. - * - * @param index The int index of the child to be removed. - * @return the node removed. - */ - - public Node removeChildAtIndex(int index) { - synchronized (Tree.this) { - Node tmpNode = (Node) children.remove(index); - Tree.this.modified(); - return tmpNode; - } - } - - /** - * Removes the specified child Node from the children - * ArrayList. Synchronized on the enclosing Tree object. - * - * Implemented by calling removeChildAtIndex(). Relies - * on that method to call the modified method of the - * containing Tree to maintain the value of modCount. - * - * @param child The child node to be removed. - * @return the node removed. - */ - - public Node removeChild(Node child) - throws NoSuchElementException { - synchronized (Tree.this) { - int index = children.indexOf((Object) child); - if (index == -1) { - throw new NoSuchElementException(); - } - Node tmpNode = removeChildAtIndex(index); - // Note - no call to Tree.this.modified() here - - // done in removeChildAtindex() - return tmpNode; - } - } - - /** - * Deletes the entire subtree rooted on this from the - * Tree. The Tree is - * traversed in PostOrder, and each Node is removed in PostOrder. - * @return int count of Nodes deleted. - */ - public int deleteSubTree() { - synchronized (Tree.this) { - int count = delete(this); - Tree.this.modified(); - return count; - } - } - - /** - * N.B. this private method must only be called from the deleteSubTree - * method, which is synchronized. In itself, it is not synchronized. - * @param subtree Node at the root of the subtree to be deleted. - * @return int count of Nodes deleted. - */ - private int delete(Node subtree) { - int count = 0; - - while (subtree.numChildren() > 0) { - //System.out.println("# children "+subtree.numChildren()); - - count += delete((Node)subtree.children.get(0)); - } - // Delete this node - // nullify the parent reference - if (subtree.getTree().getRoot() != subtree) { - // Not the root node - remove from parent - subtree.getParent().removeChild(subtree); - subtree.unsetParent(); - } else { - subtree.getTree().unsetRoot(); - } // end of else - return ++count; - } - - public Tree getTree() { - return Tree.this; - } - - public Node getParent() { - return (Node) parent; - } - - public void unsetParent() { - parent = null; - } - - public Node getChild(int index) { - return (Node) children.get(index); - } - - public Iterator nodeChildren() { - return children.iterator(); - } - - public int numChildren() { - return children.size(); - } - - /** - * Class PreOrder is a member class of Node. - * - * It implements the Iterator interface, excluding the - * optional remove method. The iterator traverses its - * containing Tree from its containing Node in - * preorder order. - * - * The method is implemented recursively; - * at each node, a PreOrder object is instantiated to handle the - * node itself and to trigger the handing of the node's children. - * The node is returned first, and then for each child, a new - * PreOrder is instantiated. That iterator will terminate when the - * last descendant of that child has been returned. - * - * The iterator is fast-fail. If any modifications occur to - * the tree as a whole during the lifetime of the iterator, a - * subsequent call to next() will throw a - * ConcurrentModificationException. See the discussion of - * fast-fail iterators in AbstractList. - * - * The modCount field used to maintain information about - * structural modifcations is maintained for all nodes in the - * containing Tree instance. - */ - - class PreOrder implements Iterator { - private boolean selfNotReturned = true; - private int nextChildIndex = 0; // N.B. this must be kept as - // the index of the active child until that child is exhausted. - // At the start of proceedings, it may already point past the - // end of the (empty) child vector - - private int age; - private PreOrder nextChildIterator; - - /** - * Constructor - * - * @param age the current value of the modCount field in the - * Tree instance which includes this class instance. - */ - public PreOrder(int age) { - this.age = age; - hasNext(); // A call to set up the initial iterators - // so that a call to next() without a preceding call to - // hasNext() will behave sanely - } - - public boolean hasNext() { - // synchronize this against possible changes to the tree - synchronized (Tree.this) { - if (selfNotReturned) { - return true; - } - // self has been returned - are there any children? - // if so, we must always have an iterator available - // even unless it is exhausted. Assume it is set up this - // way by next(). The iterator has a chance to do this - // because self will always be returned first. - // The test of nextChildIndex must always be made because - // there may be no children, or the last child may be - // exhausted, hence no possibility of an - // iterator on the children of any child. - if (nextChildIndex < children.size()) { - return nextChildIterator.hasNext(); - } - else { // no kiddies - return false; - } - } - } - - public Object next() - throws NoSuchElementException, - ConcurrentModificationException { - synchronized (Tree.this) { - // synchronize the whole against changes to the tree - - // Check for ConcurrentModification - if (! Tree.this.modCountEqualTo(age)) { - throw new ConcurrentModificationException(); - } - - if (! hasNext()) { - throw new NoSuchElementException(); - } - if (selfNotReturned) { - selfNotReturned = false; - if (nextChildIndex < children.size()) { - // We have children - create an iterator - // for the first one - nextChildIterator = - ((Node) - (children.get(nextChildIndex))).new - PreOrder(age); - } - // else do nothing; - // the nextChildIndex test in hasNext() - // will prevent us from getting into trouble - return Node.this; - } - else { // self has been returned - // there must be a next available, or we would not have - // come this far - if (! nextChildIterator.hasNext()) { - // last iterator was exhausted; - // if there was another child available, an - //iterator would already have been set up. - // Every iterator will return at least one node - - // the node on which it is defined. - // So why did the initial hasNext succeed? - throw new NoSuchElementException( - "Cannot reach this"); - } - Object tempNode = nextChildIterator.next(); - // Check for exhaustion of the child - if (! nextChildIterator.hasNext()) { - // child iterator empty - another child? - if (++nextChildIndex < children.size()) { - nextChildIterator = - ((Node) - (children.get(nextChildIndex))).new - PreOrder(age); - } - else { - // nullify the iterator - nextChildIterator = null; - } - } - return (Node) tempNode; - } - } - } - - public void remove() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - } - - - /** - * Class PostOrder is a member class of Node. - * - * It implements the Iterator interface, excluding the - * optional remove method. The iterator traverses its - * containing Tree from its containing Node in - * postorder order. - * - * The method is implemented recursively; - * at each node, a PostOrder object is instantiated to handle the - * node itself and to trigger the handing of the node's children. - * Firstly, for each child a new PostOrder is instantiated. - * That iterator will terminate when the last descendant of that - * child has been returned. finally, the node itself is returned. - * - * The iterator is fast-fail. iI any modifications occur to - * the tree as a whole during the lifetime of the iterator, a - * subsequent call to next() will throw a - * ConcurrentModificationException. See the discussion of - * fast-fail iterators in AbstractList. - * - * The modCount field used to maintain information about - * structural modifcations is maintained for all nodes in the - * containing Tree instance. - */ - - class PostOrder implements Iterator { - private boolean selfReturned = false; - private int nextChildIndex = 0; // N.B. this must be kept as - // the index of the active child until that child is exhausted. - // At the start of proceedings, it may already point past the - // end of the (empty) child vector - - private int age; - private PostOrder nextChildIterator; - - /** - * Constructor - * - * @param age the current value of the modCount field in the - * Tree instance which includes this class instance. - */ - public PostOrder(int age) { - this.age = age; - hasNext(); // A call to set up the initial iterators - // so that a call to next() without a preceding call to - // hasNext() will behave sanely - } - - public boolean hasNext() { - // Synchronize this against changes in the tree - synchronized (Tree.this) { - // self is always the last to go - if (selfReturned) { // nothing left - return false; - } - - // Check first for children, and set up an iterator if so - if (nextChildIndex < children.size()) { - if (nextChildIterator == null) { - nextChildIterator = - ((Node) - (children.get(nextChildIndex))).new - PostOrder(age); - } - // else an iterator exists. - // Assume that the next() method - // will keep the iterator current - } // end of Any children? - - return true; - } - } - - public Object next() - throws NoSuchElementException, - ConcurrentModificationException { - synchronized (Tree.this) { - // synchronize the whole against changes to the tree - - // Check for ConcurrentModification - if (! Tree.this.modCountEqualTo(age)) { - throw new ConcurrentModificationException(); - } - - if (! hasNext()) { - throw new NoSuchElementException(); - } - // Are there any children? - if (nextChildIndex < children.size()) { - // There will be an active iterator. Is it empty? - if (nextChildIterator.hasNext()) { - // children remain - Object tempNode = nextChildIterator.next(); - // now check for exhaustion of the iterator - if (! nextChildIterator.hasNext()) { - if (++nextChildIndex < children.size()) { - nextChildIterator = - ((Node) - (children.get(nextChildIndex))).new - PostOrder(age); - } - // else leave the iterator bumping on empty - // next call will return self - } - // else iterator not exhausted - // return the Node - return (Node) tempNode; - } - // else children exhausted - fall through - } - // No children - return self object - selfReturned = true; - nextChildIterator = null; - return Node.this; - } - } - - public void remove() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - } - - - /** - * Class Ancestor is a member class of Node. - * - * It implements the Iterator interface, excluding the - * optional remove method. The iterator traverses the - * ancestors of its containing Node from the Node's immediate - * parent back to tge root Node of the containing Tree. - * - * The iterator is fast-fail. If any modifications occur to - * the tree as a whole during the lifetime of the iterator, a - * subsequent call to next() will throw a - * ConcurrentModificationException. See the discussion of - * fast-fail iterators in AbstractList. - * - * The modCount field used to maintain information about - * structural modifcations is maintained for all nodes in the - * containing Tree instance. - */ - - class Ancestor implements Iterator { - private Tree.Node nextAncestor; - private int age; - - /** - * Constructor - * - * @param age the current value of the modCount field in the - * Tree instance which includes this class instance. - */ - - public Ancestor(int age) { - this.age = age; - nextAncestor = Node.this.parent; - } - - public boolean hasNext() { - return nextAncestor != null; - } - - public Object next() - throws NoSuchElementException, - ConcurrentModificationException { - synchronized (Tree.this) { - // The tree is a - // potentially dymanic structure, which could be - // undergoing modification as this method is being - // executed, and it is possible that the Comod exception - // could be set to trigger while this call is in process. - if (! Tree.this.modCountEqualTo(age)) { - throw new ConcurrentModificationException(); - } - Tree.Node tmpNode = nextAncestor; - nextAncestor = tmpNode.parent; - return tmpNode; - } - } - - public void remove() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - } - - - /** - * Class FollowingSibling is a member class of Node. - * - * It implements the ListIterator interface, but has reports - * UnsupportedOperationException for all methods except - * hasNext(), next() and nextIndex(). - * These methods are implemented as synchronized wrappers around the - * underlying ArrayList methods. - * - * The listIterator traverses those children in the parent node's - * children ArrayList which follow the subject - * node's entry in that array, using the next() method. - * - * The iterator is fail-fast. if any modification occur to - * the tree as a whole during the lifetime of the iterator, a - * subsequent call to next() will throw a - * ConcurrentModificationException. See the discussion of - * fast-fail iterators in AbstractList. - * - * The fail-fast ListIterator in ArrayList is the underlying - * mechanism for both the listIterator and the fail-fast - * behaviour. - */ - - class FollowingSibling implements ListIterator { - - private ListIterator listIterator; - private ArrayList rootDummy = new ArrayList(); - // An empty ArrayList for the root listIterator - // hasNext() will always return false - - public FollowingSibling() { - synchronized (Tree.this) { - // Set up iterator on the parent's arrayList of children - Tree.Node refNode = Node.this.parent; - if (refNode != null) { - // Not the root node; siblings may exist - // Set up iterator on the parent's children ArrayList - ArrayList siblings = refNode.children; - int index = siblings.indexOf((Object) Node.this); - // if this is invalid, we are in serious trouble - listIterator = siblings.listIterator(index + 1); - } // end of if (Node.this.parent != null) - else { - // Root node - no siblings - listIterator = rootDummy.listIterator(); - } - } - } - - public boolean hasNext() { - // Any CoMod exception will be thrown by the listIterator - // provided with ArrayList. It does not throw such exceptions - // on calls to hasNext(); - synchronized (Tree.this) { - return listIterator.hasNext(); - } - } - - public Object next() - throws NoSuchElementException, - ConcurrentModificationException { - synchronized (Tree.this) { - // N.B. synchronization here is still on the Tree - // rather than on the Node containing the children - // ArryList of interest. Other ArrayList operations - // throughout the Tree are synchronized on the Tree object - // itself, so exceptions cannot be made for these more - // directly Nodal operations. - return listIterator.next(); - } - } - - public int nextIndex() { - synchronized (Tree.this) { - return listIterator.nextIndex(); - } - } - - public void add(Object o) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public void remove() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public boolean hasPrevious() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public Object previous() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public int previousIndex() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public void set(Object o) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - } - - /** - * Class PrecedingSibling is a member class of Node. - * - * It implements the ListIterator interface, but has reports - * UnsupportedOperationException for all methods except - * hasPrevious(), previous() and - * previousIndex(). - * These methods are implemented as synchronized wrappers around the - * underlying ArrayList methods. - * - * The listIterator traverses those children in the parent node's - * children ArrayList which precede the subject - * node's entry in that array, using the previous() method. - * I.e., siblings are produced in reverse sibling order. - * - * The iterator is fail-fast. if any modification occur to - * the tree as a whole during the lifetime of the iterator, a - * subsequent call to previous() will throw a - * ConcurrentModificationException. See the discussion of - * fast-fail iterators in AbstractList. - * - * The fail-fast ListIterator in ArrayList is the underlying - * mechanism for both the listIterator and the fail-fast - * behaviour. - */ - - class PrecedingSibling implements ListIterator { - - private ListIterator listIterator; - private ArrayList rootDummy = new ArrayList(); - // An empty ArrayList for the root listIterator - // hasNext() will always return false - - public PrecedingSibling() { - synchronized (Tree.this) { - // Set up iterator on the parent's arrayList of children - Tree.Node refNode = Node.this.parent; - if (refNode != null) { - // Not the root node; siblings may exist - // Set up iterator on the parent's children ArrayList - ArrayList siblings = refNode.children; - int index = siblings.indexOf((Object) Node.this); - // if this is invalid, we are in serious trouble - listIterator = siblings.listIterator(index); - } // end of if (Node.this.parent != null) - else { - // Root node - no siblings - listIterator = rootDummy.listIterator(); - } - } - } - - public boolean hasPrevious() { - // Any CoMod exception will be thrown by the listIterator - // provided with ArrayList. It does not throw such exceptions - // on calls to hasNext(); - synchronized (Tree.this) { - return listIterator.hasPrevious(); - } - } - - public Object previous() - throws NoSuchElementException, - ConcurrentModificationException { - synchronized (Tree.this) { - // N.B. synchronization here is still on the Tree - // rather than on the Node containing the children - // ArryList of interest. Other ArrayList operations - // throughout the Tree are synchronized on the Tree object - // itself, so exceptions cannot be made for these more - // directly Nodal operations. - return listIterator.previous(); - } - } - - public int previousIndex() { - synchronized (Tree.this) { - return listIterator.previousIndex(); - } - } - - public void add(Object o) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public void remove() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public Object next() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public int nextIndex() - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public void set(Object o) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - } - + public void unsetRoot() { + root = null; } } diff --git a/src/org/apache/fop/datastructs/TreeException.java b/src/org/apache/fop/datastructs/TreeException.java new file mode 100644 index 000000000..f5201ea1e --- /dev/null +++ b/src/org/apache/fop/datastructs/TreeException.java @@ -0,0 +1,32 @@ +/* + * $Id$ + * + * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. + * For details on use and redistribution please refer to the + * LICENSE file included with these sources. + * + */ + +package org.apache.fop.datastructs; + +import org.apache.fop.apps.FOPException; + +/** + * Exceptions thrown during Tree/Node operations. + * @author Peter B. West + * @version $Revision$ $Name$ + */ + +public class TreeException extends FOPException { + private static final String tag = "$Name$"; + private static final String revision = "$Revision$"; + + public TreeException(String message) { + super(message); + } + + public TreeException(Throwable e) { + super(e); + } + +}