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