/* |
|
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
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; |
|
} |
|
} |
|
} |
|
} |