You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
10 KiB
382 lines
10 KiB
/*
|
|
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE 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, iterates 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
|
|
*
|
|
*/
|
|
|
|
public class ElementIterator implements Cloneable {
|
|
|
|
|
|
private Element root;
|
|
private Stack<StackItem> 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<StackItem>();
|
|
for (int i = 0; i < elementStack.size(); i++) {
|
|
StackItem item = elementStack.elementAt(i);
|
|
StackItem clonee = (StackItem)item.clone();
|
|
it.elementStack.push(clonee);
|
|
}
|
|
}
|
|
return it;
|
|
} catch (CloneNotSupportedException e) {
|
|
throw new InternalError(e);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Fetches the first element.
|
|
*
|
|
* @return an Element.
|
|
*/
|
|
public Element first() {
|
|
// just in case...
|
|
if (root == null) {
|
|
return null;
|
|
}
|
|
|
|
elementStack = new Stack<StackItem>();
|
|
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 = 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 = 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 = 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 however 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 = 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. */
|
|
StackItem top = elementStack.pop();
|
|
item = 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;
|
|
}
|
|
}
|
|
}
|
|
}
|