1 /* 2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.text; 27 28 import java.util.Stack; 29 import java.util.Enumeration; 30 31 /** 32 * <p> 33 * ElementIterator, as the name suggests, iterates over the Element 34 * tree. The constructor can be invoked with either Document or an Element 35 * as an argument. If the constructor is invoked with a Document as an 36 * argument then the root of the iteration is the return value of 37 * document.getDefaultRootElement(). 38 * 39 * The iteration happens in a depth-first manner. In terms of how 40 * boundary conditions are handled: 41 * a) if next() is called before first() or current(), the 42 * root will be returned. 43 * b) next() returns null to indicate the end of the list. 44 * c) previous() returns null when the current element is the root 45 * or next() has returned null. 46 * 47 * The ElementIterator does no locking of the Element tree. This means 48 * that it does not track any changes. It is the responsibility of the 49 * user of this class, to ensure that no changes happen during element 50 * iteration. 51 * 52 * Simple usage example: 53 * 54 * public void iterate() { 55 * ElementIterator it = new ElementIterator(root); 56 * Element elem; 57 * while (true) { 58 * if ((elem = next()) != null) { 59 * // process element 60 * System.out.println("elem: " + elem.getName()); 61 * } else { 62 * break; 63 * } 64 * } 65 * } 66 * 67 * @author Sunita Mani 68 * 69 */ 70 71 public class ElementIterator implements Cloneable { 72 73 74 private Element root; 75 private Stack<StackItem> elementStack = null; 76 77 /** 78 * The StackItem class stores the element 79 * as well as a child index. If the 80 * index is -1, then the element represented 81 * on the stack is the element itself. 82 * Otherwise, the index functions as as index 83 * into the vector of children of the element. 84 * In this case, the item on the stack 85 * represents the "index"th child of the element 86 * 87 */ 88 private class StackItem implements Cloneable { 89 Element item; 90 int childIndex; 91 92 private StackItem(Element elem) { 93 /** 94 * -1 index implies a self reference, 95 * as opposed to an index into its 96 * list of children. 97 */ 98 this.item = elem; 99 this.childIndex = -1; 100 } 101 102 private void incrementIndex() { 103 childIndex++; 104 } 105 106 private Element getElement() { 107 return item; 108 } 109 110 private int getIndex() { 111 return childIndex; 112 } 113 114 protected Object clone() throws java.lang.CloneNotSupportedException { 115 return super.clone(); 116 } 117 } 118 119 /** 120 * Creates a new ElementIterator. The 121 * root element is taken to get the 122 * default root element of the document. 123 * 124 * @param document a Document. 125 */ 126 public ElementIterator(Document document) { 127 root = document.getDefaultRootElement(); 128 } 129 130 131 /** 132 * Creates a new ElementIterator. 133 * 134 * @param root the root Element. 135 */ 136 public ElementIterator(Element root) { 137 this.root = root; 138 } 139 140 141 /** 142 * Clones the ElementIterator. 143 * 144 * @return a cloned ElementIterator Object. 145 */ 146 public synchronized Object clone() { 147 148 try { 149 ElementIterator it = new ElementIterator(root); 150 if (elementStack != null) { 151 it.elementStack = new Stack<StackItem>(); 152 for (int i = 0; i < elementStack.size(); i++) { 153 StackItem item = elementStack.elementAt(i); 154 StackItem clonee = (StackItem)item.clone(); 155 it.elementStack.push(clonee); 156 } 157 } 158 return it; 159 } catch (CloneNotSupportedException e) { 160 throw new InternalError(e); 161 } 162 } 163 164 165 /** 166 * Fetches the first element. 167 * 168 * @return an Element. 169 */ 170 public Element first() { 171 // just in case... 172 if (root == null) { 173 return null; 174 } 175 176 elementStack = new Stack<StackItem>(); 177 if (root.getElementCount() != 0) { 178 elementStack.push(new StackItem(root)); 179 } 180 return root; 181 } 182 183 /** 184 * Fetches the current depth of element tree. 185 * 186 * @return the depth. 187 */ 188 public int depth() { 189 if (elementStack == null) { 190 return 0; 191 } 192 return elementStack.size(); 193 } 194 195 196 /** 197 * Fetches the current Element. 198 * 199 * @return element on top of the stack or 200 * <code>null</code> if the root element is <code>null</code> 201 */ 202 public Element current() { 203 204 if (elementStack == null) { 205 return first(); 206 } 207 208 /* 209 get a handle to the element on top of the stack. 210 */ 211 if (! elementStack.empty()) { 212 StackItem item = elementStack.peek(); 213 Element elem = item.getElement(); 214 int index = item.getIndex(); 215 // self reference 216 if (index == -1) { 217 return elem; 218 } 219 // return the child at location "index". 220 return elem.getElement(index); 221 } 222 return null; 223 } 224 225 226 /** 227 * Fetches the next Element. The strategy 228 * used to locate the next element is 229 * a depth-first search. 230 * 231 * @return the next element or <code>null</code> 232 * at the end of the list. 233 */ 234 public Element next() { 235 236 /* if current() has not been invoked 237 and next is invoked, the very first 238 element will be returned. */ 239 if (elementStack == null) { 240 return first(); 241 } 242 243 // no more elements 244 if (elementStack.isEmpty()) { 245 return null; 246 } 247 248 // get a handle to the element on top of the stack 249 250 StackItem item = elementStack.peek(); 251 Element elem = item.getElement(); 252 int index = item.getIndex(); 253 254 if (index+1 < elem.getElementCount()) { 255 Element child = elem.getElement(index+1); 256 if (child.isLeaf()) { 257 /* In this case we merely want to increment 258 the child index of the item on top of the 259 stack.*/ 260 item.incrementIndex(); 261 } else { 262 /* In this case we need to push the child(branch) 263 on the stack so that we can iterate over its 264 children. */ 265 elementStack.push(new StackItem(child)); 266 } 267 return child; 268 } else { 269 /* No more children for the item on top of the 270 stack therefore pop the stack. */ 271 elementStack.pop(); 272 if (!elementStack.isEmpty()) { 273 /* Increment the child index for the item that 274 is now on top of the stack. */ 275 StackItem top = elementStack.peek(); 276 top.incrementIndex(); 277 /* We now want to return its next child, therefore 278 call next() recursively. */ 279 return next(); 280 } 281 } 282 return null; 283 } 284 285 286 /** 287 * Fetches the previous Element. If however the current 288 * element is the last element, or the current element 289 * is null, then null is returned. 290 * 291 * @return previous <code>Element</code> if available 292 * 293 */ 294 public Element previous() { 295 296 int stackSize; 297 if (elementStack == null || (stackSize = elementStack.size()) == 0) { 298 return null; 299 } 300 301 // get a handle to the element on top of the stack 302 // 303 StackItem item = elementStack.peek(); 304 Element elem = item.getElement(); 305 int index = item.getIndex(); 306 307 if (index > 0) { 308 /* return child at previous index. */ 309 return getDeepestLeaf(elem.getElement(--index)); 310 } else if (index == 0) { 311 /* this implies that current is the element's 312 first child, therefore previous is the 313 element itself. */ 314 return elem; 315 } else if (index == -1) { 316 if (stackSize == 1) { 317 // current is the root, nothing before it. 318 return null; 319 } 320 /* We need to return either the item 321 below the top item or one of the 322 former's children. */ 323 StackItem top = elementStack.pop(); 324 item = elementStack.peek(); 325 326 // restore the top item. 327 elementStack.push(top); 328 elem = item.getElement(); 329 index = item.getIndex(); 330 return ((index == -1) ? elem : getDeepestLeaf(elem.getElement 331 (index))); 332 } 333 // should never get here. 334 return null; 335 } 336 337 /** 338 * Returns the last child of <code>parent</code> that is a leaf. If the 339 * last child is a not a leaf, this method is called with the last child. 340 */ 341 private Element getDeepestLeaf(Element parent) { 342 if (parent.isLeaf()) { 343 return parent; 344 } 345 int childCount = parent.getElementCount(); 346 if (childCount == 0) { 347 return parent; 348 } 349 return getDeepestLeaf(parent.getElement(childCount - 1)); 350 } 351 352 /* 353 Iterates through the element tree and prints 354 out each element and its attributes. 355 */ 356 private void dumpTree() { 357 358 Element elem; 359 while (true) { 360 if ((elem = next()) != null) { 361 System.out.println("elem: " + elem.getName()); 362 AttributeSet attr = elem.getAttributes(); 363 String s = ""; 364 Enumeration names = attr.getAttributeNames(); 365 while (names.hasMoreElements()) { 366 Object key = names.nextElement(); 367 Object value = attr.getAttribute(key); 368 if (value instanceof AttributeSet) { 369 // don't go recursive 370 s = s + key + "=**AttributeSet** "; 371 } else { 372 s = s + key + "=" + value + " "; 373 } 374 } 375 System.out.println("attributes: " + s); 376 } else { 377 break; 378 } 379 } 380 } 381 }