< prev index next >

src/java.desktop/share/classes/javax/swing/text/ElementIterator.java

Print this page


   1 /*
   2  * Copyright (c) 1998, 2019, 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.Enumeration;
  29 import java.util.Stack;

  30 
  31 /**
  32  * {@code ElementIterator}, as the name suggests, iterates over the
  33  * {@code Element} tree. The constructor can be invoked with either
  34  * {@code Document} or an {@code Element} as an argument. If the constructor is
  35  * invoked with a {@code Document} as an argument then the root of the iteration
  36  * is the return value of {@code document.getDefaultRootElement()}.
  37  * <p>
  38  * The iteration happens in a depth-first manner. In terms of how boundary
  39  * conditions are handled:
  40  * <ul>
  41  *   <li>if {@link #next} is called before {@link #first} or {@link #current},
  42  *   the root will be returned
  43  *   <li>{@link #next} returns {@code null} to indicate the end of the list
  44  *   <li>{@link #previous} returns {@code null} when the current element is the
  45  *   root or {@link #next} has returned {@code null}
  46  * </ul>
  47  * <p>
  48  * The {@code ElementIterator} does no locking of the {@code Element} tree. This
  49  * means that it does not track any changes. It is the responsibility of the














  50  * user of this class, to ensure that no changes happen during element
  51  * iteration.
  52  * <p>
  53  * Simple usage example:
  54  * <pre>{@code public void iterate() {
  55  *      ElementIterator it = new ElementIterator(root);
  56  *      Element elem;
  57  *      while (true) {
  58  *          if ((elem = it.next()) != null) {
  59  *              // process element
  60  *              System.out.println("elem: " + elem.getName());
  61  *          } else {
  62  *              break;
  63  *          }
  64  *      }
  65  * }}</pre>

  66  *
  67  * @author Sunita Mani

  68  */

  69 public class ElementIterator implements Cloneable {
  70 

  71     private Element root;
  72     private Stack<StackItem> elementStack = null;
  73 
  74     /**
  75      * The {@code StackItem} class stores the element as well as a child index.
  76      * If the index is -1, then the element represented on the stack is the
  77      * element itself. Otherwise, the index functions as an index into the
  78      * vector of children of the element. In this case, the item on the stack
  79      * represents the "index"th child of the element.




  80      */
  81     private class StackItem implements Cloneable {
  82         Element item;
  83         int childIndex;
  84 
  85         private StackItem(Element elem) {
  86             /**
  87              * -1 index implies a self reference,
  88              * as opposed to an index into its
  89              * list of children.
  90              */
  91             this.item = elem;
  92             this.childIndex = -1;
  93         }
  94 
  95         private void incrementIndex() {
  96             childIndex++;
  97         }
  98 
  99         private Element getElement() {
 100             return item;
 101         }
 102 
 103         private int getIndex() {
 104             return childIndex;
 105         }
 106 
 107         protected Object clone() throws java.lang.CloneNotSupportedException {
 108             return super.clone();
 109         }
 110     }
 111 
 112     /**
 113      * Creates a new {@code ElementIterator}. The root element is taken to get
 114      * the default root element of the document.

 115      *
 116      * @param  document a {@code Document}
 117      */
 118     public ElementIterator(Document document) {
 119         root = document.getDefaultRootElement();
 120     }
 121 

 122     /**
 123      * Creates a new {@code ElementIterator}.
 124      *
 125      * @param  root the root {@code Element}
 126      */
 127     public ElementIterator(Element root) {
 128         this.root = root;
 129     }
 130 

 131     /**
 132      * Clones the {@code ElementIterator}.
 133      *
 134      * @return a cloned {@code ElementIterator} Object
 135      */
 136     public synchronized Object clone() {
 137 
 138         try {
 139             ElementIterator it = new ElementIterator(root);
 140             if (elementStack != null) {
 141                 it.elementStack = new Stack<StackItem>();
 142                 for (int i = 0; i < elementStack.size(); i++) {
 143                     StackItem item = elementStack.elementAt(i);
 144                     StackItem clonee = (StackItem)item.clone();
 145                     it.elementStack.push(clonee);
 146                 }
 147             }
 148             return it;
 149         } catch (CloneNotSupportedException e) {
 150             throw new InternalError(e);
 151         }
 152     }
 153 

 154     /**
 155      * Fetches the first element.
 156      *
 157      * @return an {@code Element}
 158      */
 159     public Element first() {
 160         // just in case...
 161         if (root == null) {
 162             return null;
 163         }
 164 
 165         elementStack = new Stack<StackItem>();
 166         if (root.getElementCount() != 0) {
 167             elementStack.push(new StackItem(root));
 168         }
 169         return root;
 170     }
 171 
 172     /**
 173      * Fetches the current depth of element tree.
 174      *
 175      * @return the depth
 176      */
 177     public int depth() {
 178         if (elementStack == null) {
 179             return 0;
 180         }
 181         return elementStack.size();
 182     }
 183 

 184     /**
 185      * Fetches the current {@code Element}.
 186      *
 187      * @return element on top of the stack or {@code null} if the root element
 188      *         is {@code null}
 189      */
 190     public Element current() {
 191 
 192         if (elementStack == null) {
 193             return first();
 194         }
 195 
 196         /*
 197           get a handle to the element on top of the stack.
 198         */
 199         if (! elementStack.empty()) {
 200             StackItem item = elementStack.peek();
 201             Element elem = item.getElement();
 202             int index = item.getIndex();
 203             // self reference
 204             if (index == -1) {
 205                 return elem;
 206             }
 207             // return the child at location "index".
 208             return elem.getElement(index);
 209         }
 210         return null;
 211     }
 212 

 213     /**
 214      * Fetches the next {@code Element}. The strategy used to locate the next
 215      * element is a depth-first search.

 216      *
 217      * @return the next element or {@code null} at the end of the list

 218      */
 219     public Element next() {
 220 
 221         /* if current() has not been invoked
 222            and next is invoked, the very first
 223            element will be returned. */
 224         if (elementStack == null) {
 225             return first();
 226         }
 227 
 228         // no more elements
 229         if (elementStack.isEmpty()) {
 230             return null;
 231         }
 232 
 233         // get a handle to the element on top of the stack
 234 
 235         StackItem item = elementStack.peek();
 236         Element elem = item.getElement();
 237         int index = item.getIndex();


 250                 elementStack.push(new StackItem(child));
 251             }
 252             return child;
 253         } else {
 254             /* No more children for the item on top of the
 255                stack therefore pop the stack. */
 256             elementStack.pop();
 257             if (!elementStack.isEmpty()) {
 258                 /* Increment the child index for the item that
 259                    is now on top of the stack. */
 260                 StackItem top = elementStack.peek();
 261                 top.incrementIndex();
 262                 /* We now want to return its next child, therefore
 263                    call next() recursively. */
 264                 return next();
 265             }
 266         }
 267         return null;
 268     }
 269 

 270     /**
 271      * Fetches the previous {@code Element}. If however the current element is
 272      * the last element, or the current element is {@code null}, then
 273      * {@code null} is returned.


 274      *
 275      * @return previous {@code Element} if available
 276      */
 277     public Element previous() {
 278 
 279         int stackSize;
 280         if (elementStack == null || (stackSize = elementStack.size()) == 0) {
 281             return null;
 282         }
 283 
 284         // get a handle to the element on top of the stack
 285         //
 286         StackItem item = elementStack.peek();
 287         Element elem = item.getElement();
 288         int index = item.getIndex();
 289 
 290         if (index > 0) {
 291             /* return child at previous index. */
 292             return getDeepestLeaf(elem.getElement(--index));
 293         } else if (index == 0) {
 294             /* this implies that current is the element's
 295                first child, therefore previous is the


 301                 return null;
 302             }
 303             /* We need to return either the item
 304                below the top item or one of the
 305                former's children. */
 306             StackItem top = elementStack.pop();
 307             item = elementStack.peek();
 308 
 309             // restore the top item.
 310             elementStack.push(top);
 311             elem = item.getElement();
 312             index = item.getIndex();
 313             return ((index == -1) ? elem : getDeepestLeaf(elem.getElement
 314                                                           (index)));
 315         }
 316         // should never get here.
 317         return null;
 318     }
 319 
 320     /**
 321      * Returns the last child of {@code parent} that is a leaf. If the last
 322      * child is a not a leaf, this method is called with the last child.
 323      */
 324     private Element getDeepestLeaf(Element parent) {
 325         if (parent.isLeaf()) {
 326             return parent;
 327         }
 328         int childCount = parent.getElementCount();
 329         if (childCount == 0) {
 330             return parent;
 331         }
 332         return getDeepestLeaf(parent.getElement(childCount - 1));
 333     }
 334 
 335     /**
 336      * Iterates through the element tree and prints out each element and its
 337      * attributes.
 338      */
 339     private void dumpTree() {
 340 
 341         Element elem;
 342         while (true) {
 343             if ((elem = next()) != null) {
 344                 System.out.println("elem: " + elem.getName());
 345                 AttributeSet attr = elem.getAttributes();
 346                 String s = "";
 347                 Enumeration<?> names = attr.getAttributeNames();
 348                 while (names.hasMoreElements()) {
 349                     Object key = names.nextElement();
 350                     Object value = attr.getAttribute(key);
 351                     if (value instanceof AttributeSet) {
 352                         // don't go recursive
 353                         s = s + key + "=**AttributeSet** ";
 354                     } else {
 355                         s = s + key + "=" + value + " ";
 356                     }
 357                 }
 358                 System.out.println("attributes: " + s);
   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 an 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();


 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


 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);
< prev index next >