- /*
 - * @(#)ElementIterator.java 1.14 03/12/19
 - *
 - * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 - */
 - package javax.swing.text;
 - import java.util.Stack;
 - import java.util.Enumeration;
 - /**
 - * <p>
 - * ElementIterator, as the name suggests, iteratates over the Element
 - * tree. The constructor can be invoked with either Document or an Element
 - * as an argument. If the constructor is invoked with a Document as an
 - * argument then the root of the iteration is the return value of
 - * document.getDefaultRootElement().
 - *
 - * The iteration happens in a depth-first manner. In terms of how
 - * boundary conditions are handled:
 - * a) if next() is called before first() or current(), the
 - * root will be returned.
 - * b) next() returns null to indicate the end of the list.
 - * c) previous() returns null when the current element is the root
 - * or next() has returned null.
 - *
 - * The ElementIterator does no locking of the Element tree. This means
 - * that it does not track any changes. It is the responsibility of the
 - * user of this class, to ensure that no changes happen during element
 - * iteration.
 - *
 - * Simple usage example:
 - *
 - * public void iterate() {
 - * ElementIterator it = new ElementIterator(root);
 - * Element elem;
 - * while (true) {
 - * if ((elem = next()) != null) {
 - * // process element
 - * System.out.println("elem: " + elem.getName());
 - * } else {
 - * break;
 - * }
 - * }
 - * }
 - *
 - * @author Sunita Mani
 - * @version 1.14 12/19/03
 - *
 - */
 - public class ElementIterator implements Cloneable {
 - private Element root;
 - private Stack elementStack = null;
 - /**
 - * The StackItem class stores the element
 - * as well as a child index. If the
 - * index is -1, then the element represented
 - * on the stack is the element itself.
 - * Otherwise, the index functions as as index
 - * into the vector of children of the element.
 - * In this case, the item on the stack
 - * represents the "index"th child of the element
 - *
 - */
 - private class StackItem implements Cloneable {
 - Element item;
 - int childIndex;
 - private StackItem(Element elem) {
 - /**
 - * -1 index implies a self reference,
 - * as opposed to an index into its
 - * list of children.
 - */
 - this.item = elem;
 - this.childIndex = -1;
 - }
 - private void incrementIndex() {
 - childIndex++;
 - }
 - private Element getElement() {
 - return item;
 - }
 - private int getIndex() {
 - return childIndex;
 - }
 - protected Object clone() throws java.lang.CloneNotSupportedException {
 - return super.clone();
 - }
 - }
 - /**
 - * Creates a new ElementIterator. The
 - * root element is taken to get the
 - * default root element of the document.
 - *
 - * @param document a Document.
 - */
 - public ElementIterator(Document document) {
 - root = document.getDefaultRootElement();
 - }
 - /**
 - * Creates a new ElementIterator.
 - *
 - * @param root the root Element.
 - */
 - public ElementIterator(Element root) {
 - this.root = root;
 - }
 - /**
 - * Clones the ElementIterator.
 - *
 - * @return a cloned ElementIterator Object.
 - */
 - public synchronized Object clone() {
 - try {
 - ElementIterator it = new ElementIterator(root);
 - if (elementStack != null) {
 - it.elementStack = new Stack();
 - for (int i = 0; i < elementStack.size(); i++) {
 - StackItem item = (StackItem)elementStack.elementAt(i);
 - StackItem clonee = (StackItem)item.clone();
 - it.elementStack.push(clonee);
 - }
 - }
 - return it;
 - } catch (CloneNotSupportedException e) {
 - throw new InternalError();
 - }
 - }
 - /**
 - * Fetches the first element.
 - *
 - * @return an Element.
 - */
 - public Element first() {
 - // just in case...
 - if (root == null) {
 - return null;
 - }
 - elementStack = new Stack();
 - if (root.getElementCount() != 0) {
 - elementStack.push(new StackItem(root));
 - }
 - return root;
 - }
 - /**
 - * Fetches the current depth of element tree.
 - *
 - * @return the depth.
 - */
 - public int depth() {
 - if (elementStack == null) {
 - return 0;
 - }
 - return elementStack.size();
 - }
 - /**
 - * Fetches the current Element.
 - *
 - * @return element on top of the stack or
 - * <code>null</code> if the root element is <code>null</code>
 - */
 - public Element current() {
 - if (elementStack == null) {
 - return first();
 - }
 - /*
 - get a handle to the element on top of the stack.
 - */
 - if (! elementStack.empty()) {
 - StackItem item = (StackItem)elementStack.peek();
 - Element elem = item.getElement();
 - int index = item.getIndex();
 - // self reference
 - if (index == -1) {
 - return elem;
 - }
 - // return the child at location "index".
 - return elem.getElement(index);
 - }
 - return null;
 - }
 - /**
 - * Fetches the next Element. The strategy
 - * used to locate the next element is
 - * a depth-first search.
 - *
 - * @return the next element or <code>null</code>
 - * at the end of the list.
 - */
 - public Element next() {
 - /* if current() has not been invoked
 - and next is invoked, the very first
 - element will be returned. */
 - if (elementStack == null) {
 - return first();
 - }
 - // no more elements
 - if (elementStack.isEmpty()) {
 - return null;
 - }
 - // get a handle to the element on top of the stack
 - StackItem item = (StackItem)elementStack.peek();
 - Element elem = item.getElement();
 - int index = item.getIndex();
 - if (index+1 < elem.getElementCount()) {
 - Element child = elem.getElement(index+1);
 - if (child.isLeaf()) {
 - /* In this case we merely want to increment
 - the child index of the item on top of the
 - stack.*/
 - item.incrementIndex();
 - } else {
 - /* In this case we need to push the child(branch)
 - on the stack so that we can iterate over its
 - children. */
 - elementStack.push(new StackItem(child));
 - }
 - return child;
 - } else {
 - /* No more children for the item on top of the
 - stack therefore pop the stack. */
 - elementStack.pop();
 - if (!elementStack.isEmpty()) {
 - /* Increment the child index for the item that
 - is now on top of the stack. */
 - StackItem top = (StackItem)elementStack.peek();
 - top.incrementIndex();
 - /* We now want to return its next child, therefore
 - call next() recursively. */
 - return next();
 - }
 - }
 - return null;
 - }
 - /**
 - * Fetches the previous Element. If howver the current
 - * element is the last element, or the current element
 - * is null, then null is returned.
 - *
 - * @return previous <code>Element</code> if available
 - *
 - */
 - public Element previous() {
 - int stackSize;
 - if (elementStack == null || (stackSize = elementStack.size()) == 0) {
 - return null;
 - }
 - // get a handle to the element on top of the stack
 - //
 - StackItem item = (StackItem)elementStack.peek();
 - Element elem = item.getElement();
 - int index = item.getIndex();
 - if (index > 0) {
 - /* return child at previous index. */
 - return getDeepestLeaf(elem.getElement(--index));
 - } else if (index == 0) {
 - /* this implies that current is the element's
 - first child, therefore previous is the
 - element itself. */
 - return elem;
 - } else if (index == -1) {
 - if (stackSize == 1) {
 - // current is the root, nothing before it.
 - return null;
 - }
 - /* We need to return either the item
 - below the top item or one of the
 - former's children. */
 - Object top = elementStack.pop();
 - item = (StackItem)elementStack.peek();
 - // restore the top item.
 - elementStack.push(top);
 - elem = item.getElement();
 - index = item.getIndex();
 - return ((index == -1) ? elem : getDeepestLeaf(elem.getElement
 - (index)));
 - }
 - // should never get here.
 - return null;
 - }
 - /**
 - * Returns the last child of <code>parent</code> that is a leaf. If the
 - * last child is a not a leaf, this method is called with the last child.
 - */
 - private Element getDeepestLeaf(Element parent) {
 - if (parent.isLeaf()) {
 - return parent;
 - }
 - int childCount = parent.getElementCount();
 - if (childCount == 0) {
 - return parent;
 - }
 - return getDeepestLeaf(parent.getElement(childCount - 1));
 - }
 - /*
 - Iterates through the element tree and prints
 - out each element and its attributes.
 - */
 - private void dumpTree() {
 - Element elem;
 - while (true) {
 - if ((elem = next()) != null) {
 - System.out.println("elem: " + elem.getName());
 - AttributeSet attr = elem.getAttributes();
 - String s = "";
 - Enumeration names = attr.getAttributeNames();
 - while (names.hasMoreElements()) {
 - Object key = names.nextElement();
 - Object value = attr.getAttribute(key);
 - if (value instanceof AttributeSet) {
 - // don't go recursive
 - s = s + key + "=**AttributeSet** ";
 - } else {
 - s = s + key + "=" + value + " ";
 - }
 - }
 - System.out.println("attributes: " + s);
 - } else {
 - break;
 - }
 - }
 - }
 - }