/* * Copyright (c) 1998, 2008, 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, 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 * */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(); } } /** * 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 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 = 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; } } }}
/*
* Copyright (c) 1998, 2008, 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, 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
*
*/
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();
}
}
/**
* 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 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 = 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;
}
}
}
}