1. /*
  2. * @(#)ElementIterator.java 1.14 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text;
  8. import java.util.Stack;
  9. import java.util.Enumeration;
  10. /**
  11. * <p>
  12. * ElementIterator, as the name suggests, iteratates over the Element
  13. * tree. The constructor can be invoked with either Document or an Element
  14. * as an argument. If the constructor is invoked with a Document as an
  15. * argument then the root of the iteration is the return value of
  16. * document.getDefaultRootElement().
  17. *
  18. * The iteration happens in a depth-first manner. In terms of how
  19. * boundary conditions are handled:
  20. * a) if next() is called before first() or current(), the
  21. * root will be returned.
  22. * b) next() returns null to indicate the end of the list.
  23. * c) previous() returns null when the current element is the root
  24. * or next() has returned null.
  25. *
  26. * The ElementIterator does no locking of the Element tree. This means
  27. * that it does not track any changes. It is the responsibility of the
  28. * user of this class, to ensure that no changes happen during element
  29. * iteration.
  30. *
  31. * Simple usage example:
  32. *
  33. * public void iterate() {
  34. * ElementIterator it = new ElementIterator(root);
  35. * Element elem;
  36. * while (true) {
  37. * if ((elem = next()) != null) {
  38. * // process element
  39. * System.out.println("elem: " + elem.getName());
  40. * } else {
  41. * break;
  42. * }
  43. * }
  44. * }
  45. *
  46. * @author Sunita Mani
  47. * @version 1.14 12/19/03
  48. *
  49. */
  50. public class ElementIterator implements Cloneable {
  51. private Element root;
  52. private Stack elementStack = null;
  53. /**
  54. * The StackItem class stores the element
  55. * as well as a child index. If the
  56. * index is -1, then the element represented
  57. * on the stack is the element itself.
  58. * Otherwise, the index functions as as index
  59. * into the vector of children of the element.
  60. * In this case, the item on the stack
  61. * represents the "index"th child of the element
  62. *
  63. */
  64. private class StackItem implements Cloneable {
  65. Element item;
  66. int childIndex;
  67. private StackItem(Element elem) {
  68. /**
  69. * -1 index implies a self reference,
  70. * as opposed to an index into its
  71. * list of children.
  72. */
  73. this.item = elem;
  74. this.childIndex = -1;
  75. }
  76. private void incrementIndex() {
  77. childIndex++;
  78. }
  79. private Element getElement() {
  80. return item;
  81. }
  82. private int getIndex() {
  83. return childIndex;
  84. }
  85. protected Object clone() throws java.lang.CloneNotSupportedException {
  86. return super.clone();
  87. }
  88. }
  89. /**
  90. * Creates a new ElementIterator. The
  91. * root element is taken to get the
  92. * default root element of the document.
  93. *
  94. * @param document a Document.
  95. */
  96. public ElementIterator(Document document) {
  97. root = document.getDefaultRootElement();
  98. }
  99. /**
  100. * Creates a new ElementIterator.
  101. *
  102. * @param root the root Element.
  103. */
  104. public ElementIterator(Element root) {
  105. this.root = root;
  106. }
  107. /**
  108. * Clones the ElementIterator.
  109. *
  110. * @return a cloned ElementIterator Object.
  111. */
  112. public synchronized Object clone() {
  113. try {
  114. ElementIterator it = new ElementIterator(root);
  115. if (elementStack != null) {
  116. it.elementStack = new Stack();
  117. for (int i = 0; i < elementStack.size(); i++) {
  118. StackItem item = (StackItem)elementStack.elementAt(i);
  119. StackItem clonee = (StackItem)item.clone();
  120. it.elementStack.push(clonee);
  121. }
  122. }
  123. return it;
  124. } catch (CloneNotSupportedException e) {
  125. throw new InternalError();
  126. }
  127. }
  128. /**
  129. * Fetches the first element.
  130. *
  131. * @return an Element.
  132. */
  133. public Element first() {
  134. // just in case...
  135. if (root == null) {
  136. return null;
  137. }
  138. elementStack = new Stack();
  139. if (root.getElementCount() != 0) {
  140. elementStack.push(new StackItem(root));
  141. }
  142. return root;
  143. }
  144. /**
  145. * Fetches the current depth of element tree.
  146. *
  147. * @return the depth.
  148. */
  149. public int depth() {
  150. if (elementStack == null) {
  151. return 0;
  152. }
  153. return elementStack.size();
  154. }
  155. /**
  156. * Fetches the current Element.
  157. *
  158. * @return element on top of the stack or
  159. * <code>null</code> if the root element is <code>null</code>
  160. */
  161. public Element current() {
  162. if (elementStack == null) {
  163. return first();
  164. }
  165. /*
  166. get a handle to the element on top of the stack.
  167. */
  168. if (! elementStack.empty()) {
  169. StackItem item = (StackItem)elementStack.peek();
  170. Element elem = item.getElement();
  171. int index = item.getIndex();
  172. // self reference
  173. if (index == -1) {
  174. return elem;
  175. }
  176. // return the child at location "index".
  177. return elem.getElement(index);
  178. }
  179. return null;
  180. }
  181. /**
  182. * Fetches the next Element. The strategy
  183. * used to locate the next element is
  184. * a depth-first search.
  185. *
  186. * @return the next element or <code>null</code>
  187. * at the end of the list.
  188. */
  189. public Element next() {
  190. /* if current() has not been invoked
  191. and next is invoked, the very first
  192. element will be returned. */
  193. if (elementStack == null) {
  194. return first();
  195. }
  196. // no more elements
  197. if (elementStack.isEmpty()) {
  198. return null;
  199. }
  200. // get a handle to the element on top of the stack
  201. StackItem item = (StackItem)elementStack.peek();
  202. Element elem = item.getElement();
  203. int index = item.getIndex();
  204. if (index+1 < elem.getElementCount()) {
  205. Element child = elem.getElement(index+1);
  206. if (child.isLeaf()) {
  207. /* In this case we merely want to increment
  208. the child index of the item on top of the
  209. stack.*/
  210. item.incrementIndex();
  211. } else {
  212. /* In this case we need to push the child(branch)
  213. on the stack so that we can iterate over its
  214. children. */
  215. elementStack.push(new StackItem(child));
  216. }
  217. return child;
  218. } else {
  219. /* No more children for the item on top of the
  220. stack therefore pop the stack. */
  221. elementStack.pop();
  222. if (!elementStack.isEmpty()) {
  223. /* Increment the child index for the item that
  224. is now on top of the stack. */
  225. StackItem top = (StackItem)elementStack.peek();
  226. top.incrementIndex();
  227. /* We now want to return its next child, therefore
  228. call next() recursively. */
  229. return next();
  230. }
  231. }
  232. return null;
  233. }
  234. /**
  235. * Fetches the previous Element. If howver the current
  236. * element is the last element, or the current element
  237. * is null, then null is returned.
  238. *
  239. * @return previous <code>Element</code> if available
  240. *
  241. */
  242. public Element previous() {
  243. int stackSize;
  244. if (elementStack == null || (stackSize = elementStack.size()) == 0) {
  245. return null;
  246. }
  247. // get a handle to the element on top of the stack
  248. //
  249. StackItem item = (StackItem)elementStack.peek();
  250. Element elem = item.getElement();
  251. int index = item.getIndex();
  252. if (index > 0) {
  253. /* return child at previous index. */
  254. return getDeepestLeaf(elem.getElement(--index));
  255. } else if (index == 0) {
  256. /* this implies that current is the element's
  257. first child, therefore previous is the
  258. element itself. */
  259. return elem;
  260. } else if (index == -1) {
  261. if (stackSize == 1) {
  262. // current is the root, nothing before it.
  263. return null;
  264. }
  265. /* We need to return either the item
  266. below the top item or one of the
  267. former's children. */
  268. Object top = elementStack.pop();
  269. item = (StackItem)elementStack.peek();
  270. // restore the top item.
  271. elementStack.push(top);
  272. elem = item.getElement();
  273. index = item.getIndex();
  274. return ((index == -1) ? elem : getDeepestLeaf(elem.getElement
  275. (index)));
  276. }
  277. // should never get here.
  278. return null;
  279. }
  280. /**
  281. * Returns the last child of <code>parent</code> that is a leaf. If the
  282. * last child is a not a leaf, this method is called with the last child.
  283. */
  284. private Element getDeepestLeaf(Element parent) {
  285. if (parent.isLeaf()) {
  286. return parent;
  287. }
  288. int childCount = parent.getElementCount();
  289. if (childCount == 0) {
  290. return parent;
  291. }
  292. return getDeepestLeaf(parent.getElement(childCount - 1));
  293. }
  294. /*
  295. Iterates through the element tree and prints
  296. out each element and its attributes.
  297. */
  298. private void dumpTree() {
  299. Element elem;
  300. while (true) {
  301. if ((elem = next()) != null) {
  302. System.out.println("elem: " + elem.getName());
  303. AttributeSet attr = elem.getAttributes();
  304. String s = "";
  305. Enumeration names = attr.getAttributeNames();
  306. while (names.hasMoreElements()) {
  307. Object key = names.nextElement();
  308. Object value = attr.getAttribute(key);
  309. if (value instanceof AttributeSet) {
  310. // don't go recursive
  311. s = s + key + "=**AttributeSet** ";
  312. } else {
  313. s = s + key + "=" + value + " ";
  314. }
  315. }
  316. System.out.println("attributes: " + s);
  317. } else {
  318. break;
  319. }
  320. }
  321. }
  322. }