Home | History | Annotate | Download | only in ast
      1 /*
      2  * Copyright (C) 2007-2010 Jlio Vilmar Gesser.
      3  * Copyright (C) 2011, 2013-2016 The JavaParser Team.
      4  *
      5  * This file is part of JavaParser.
      6  *
      7  * JavaParser can be used either under the terms of
      8  * a) the GNU Lesser General Public License as published by
      9  *     the Free Software Foundation, either version 3 of the License, or
     10  *     (at your option) any later version.
     11  * b) the terms of the Apache License
     12  *
     13  * You should have received a copy of both licenses in LICENCE.LGPL and
     14  * LICENCE.APACHE. Please refer to those files for details.
     15  *
     16  * JavaParser is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19  * GNU Lesser General Public License for more details.
     20  */
     21 package com.github.javaparser.ast;
     22 
     23 import com.github.javaparser.HasParentNode;
     24 import com.github.javaparser.Range;
     25 import com.github.javaparser.TokenRange;
     26 import com.github.javaparser.ast.comments.BlockComment;
     27 import com.github.javaparser.ast.comments.Comment;
     28 import com.github.javaparser.ast.comments.LineComment;
     29 import com.github.javaparser.ast.nodeTypes.NodeWithRange;
     30 import com.github.javaparser.ast.nodeTypes.NodeWithTokenRange;
     31 import com.github.javaparser.ast.observer.AstObserver;
     32 import com.github.javaparser.ast.observer.ObservableProperty;
     33 import com.github.javaparser.ast.observer.PropagatingAstObserver;
     34 import com.github.javaparser.ast.visitor.CloneVisitor;
     35 import com.github.javaparser.ast.visitor.EqualsVisitor;
     36 import com.github.javaparser.ast.visitor.HashCodeVisitor;
     37 import com.github.javaparser.ast.visitor.Visitable;
     38 import com.github.javaparser.metamodel.*;
     39 import com.github.javaparser.printer.PrettyPrinter;
     40 import com.github.javaparser.printer.PrettyPrinterConfiguration;
     41 import com.github.javaparser.resolution.SymbolResolver;
     42 import javax.annotation.Generated;
     43 import java.util.*;
     44 import java.util.function.Consumer;
     45 import java.util.function.Function;
     46 import java.util.function.Predicate;
     47 import java.util.stream.Stream;
     48 import java.util.stream.StreamSupport;
     49 import static com.github.javaparser.ast.Node.Parsedness.PARSED;
     50 import static com.github.javaparser.ast.Node.TreeTraversal.PREORDER;
     51 import static java.util.Collections.unmodifiableList;
     52 import static java.util.Spliterator.DISTINCT;
     53 import static java.util.Spliterator.NONNULL;
     54 import com.github.javaparser.ast.Node;
     55 import com.github.javaparser.metamodel.NodeMetaModel;
     56 import com.github.javaparser.metamodel.JavaParserMetaModel;
     57 
     58 /**
     59  * Base class for all nodes of the abstract syntax tree.
     60  * <h2>Construction</h2>
     61  * <p>The tree is built by instantiating the required nodes, then adding them to other nodes.
     62  * If it is the parser who is building the tree, it will use the largest constructor,
     63  * the one with "range" as the first parameter.
     64  * If you want to manually instantiate nodes, we suggest to...
     65  * <ul>
     66  * <li>use a convenience method, like "addStatement(...)", or if none are available...</li>
     67  * <li>use a convenient constructor, like ClassOrInterfaceType(String name), or if none are available...</li>
     68  * <li>use the default constructor.</li>
     69  * <li>Alternatively, use one of the JavaParser.parse(snippet) methods.</li>
     70  * </ul>
     71  * ... and use the various methods on the node to initialize it further, if needed.
     72  * <h2>Parent/child</h2>
     73  * <p>The parent node field is managed automatically and can be seen as read only.
     74  * Note that there is only one parent,
     75  * and trying to use the same node in two places will lead to unexpected behaviour.
     76  * It is advised to clone() a node before moving it around.
     77  * <h2>Comments</h2>
     78  * <p>Each Node can have one associated comment which describes it and
     79  * a number of "orphan comments" which it contains but are not specifically
     80  * associated to any child.
     81  * <h2>Positions</h2>
     82  * <p>When the parser creates nodes, it sets their source code position in the "range" field.
     83  * When you manually instantiate nodes, their range is not set.
     84  * The top left character is position 1, 1.
     85  * Note that since this is an <i>abstract</i> syntax tree,
     86  * it leaves out a lot of text from the original source file,
     87  * like where braces or comma's are exactly.
     88  * Therefore there is no position information on everything in the original source file.
     89  * <h2>Observers</h2>
     90  * <p>It is possible to add observers to the the tree.
     91  * Any change in the tree is sent as an event to any observers watching.
     92  * <h2>Visitors</h2>
     93  * <p>The most comfortable way of working with an abstract syntax tree is using visitors.
     94  * You can use one of the visitors in the visitor package, or extend one of them.
     95  * A visitor can be "run" by calling accept on a node:
     96  * <pre>node.accept(visitor, argument);</pre>
     97  * where argument is an object of your choice (often simply null.)
     98  *
     99  * @author Julio Vilmar Gesser
    100  */
    101 public abstract class Node implements Cloneable, HasParentNode<Node>, Visitable, NodeWithRange<Node>, NodeWithTokenRange<Node> {
    102 
    103     /**
    104      * Different registration mode for observers on nodes.
    105      */
    106     public enum ObserverRegistrationMode {
    107 
    108         /**
    109          * Notify exclusively for changes happening on this node alone.
    110          */
    111         JUST_THIS_NODE,
    112         /**
    113          * Notify for changes happening on this node and all its descendants existing at the moment in
    114          * which the observer was registered. Nodes attached later will not be observed.
    115          */
    116         THIS_NODE_AND_EXISTING_DESCENDANTS,
    117         /**
    118          * Notify for changes happening on this node and all its descendants. The descendants existing at the moment in
    119          * which the observer was registered will be observed immediately. As new nodes are attached later they are
    120          * automatically registered to be observed.
    121          */
    122         SELF_PROPAGATING
    123     }
    124 
    125     public enum Parsedness {
    126 
    127         PARSED, UNPARSABLE
    128     }
    129 
    130     /**
    131      * This can be used to sort nodes on position.
    132      */
    133     public static Comparator<NodeWithRange<?>> NODE_BY_BEGIN_POSITION = (a, b) -> {
    134         if (a.getRange().isPresent() && b.getRange().isPresent()) {
    135             return a.getRange().get().begin.compareTo(b.getRange().get().begin);
    136         }
    137         if (a.getRange().isPresent() || b.getRange().isPresent()) {
    138             if (a.getRange().isPresent()) {
    139                 return 1;
    140             }
    141             return -1;
    142         }
    143         return 0;
    144     };
    145 
    146     private static final PrettyPrinter toStringPrinter = new PrettyPrinter(new PrettyPrinterConfiguration());
    147 
    148     protected static final PrettyPrinterConfiguration prettyPrinterNoCommentsConfiguration = new PrettyPrinterConfiguration().setPrintComments(false);
    149 
    150     @InternalProperty
    151     private Range range;
    152 
    153     @InternalProperty
    154     private TokenRange tokenRange;
    155 
    156     @InternalProperty
    157     private Node parentNode;
    158 
    159     @InternalProperty
    160     private List<Node> childNodes = new LinkedList<>();
    161 
    162     @InternalProperty
    163     private List<Comment> orphanComments = new LinkedList<>();
    164 
    165     @InternalProperty
    166     private IdentityHashMap<DataKey<?>, Object> data = null;
    167 
    168     @OptionalProperty
    169     private Comment comment;
    170 
    171     @InternalProperty
    172     private List<AstObserver> observers = new ArrayList<>();
    173 
    174     @InternalProperty
    175     private Parsedness parsed = PARSED;
    176 
    177     protected Node(TokenRange tokenRange) {
    178         setTokenRange(tokenRange);
    179     }
    180 
    181     /**
    182      * Called in every constructor for node specific code.
    183      * It can't be written in the constructor itself because it will
    184      * be overwritten during code generation.
    185      */
    186     protected void customInitialization() {
    187     }
    188 
    189     /**
    190      * This is a comment associated with this node.
    191      *
    192      * @return comment property
    193      */
    194     @Generated("com.github.javaparser.generator.core.node.PropertyGenerator")
    195     public Optional<Comment> getComment() {
    196         return Optional.ofNullable(comment);
    197     }
    198 
    199     /**
    200      * @return the range of characters in the source code that this node covers.
    201      */
    202     public Optional<Range> getRange() {
    203         return Optional.ofNullable(range);
    204     }
    205 
    206     /**
    207      * @return the range of tokens that this node covers.
    208      */
    209     public Optional<TokenRange> getTokenRange() {
    210         return Optional.ofNullable(tokenRange);
    211     }
    212 
    213     public Node setTokenRange(TokenRange tokenRange) {
    214         this.tokenRange = tokenRange;
    215         if (tokenRange == null || !(tokenRange.getBegin().getRange().isPresent() && tokenRange.getBegin().getRange().isPresent())) {
    216             range = null;
    217         } else {
    218             range = new Range(tokenRange.getBegin().getRange().get().begin, tokenRange.getEnd().getRange().get().end);
    219         }
    220         return this;
    221     }
    222 
    223     /**
    224      * @param range the range of characters in the source code that this node covers. null can be used to indicate that
    225      * no range information is known, or that it is not of interest.
    226      */
    227     public Node setRange(Range range) {
    228         if (this.range == range) {
    229             return this;
    230         }
    231         notifyPropertyChange(ObservableProperty.RANGE, this.range, range);
    232         this.range = range;
    233         return this;
    234     }
    235 
    236     /**
    237      * Use this to store additional information to this node.
    238      *
    239      * @param comment to be set
    240      */
    241     public final Node setComment(final Comment comment) {
    242         if (this.comment == comment) {
    243             return this;
    244         }
    245         if (comment != null && (this instanceof Comment)) {
    246             throw new RuntimeException("A comment can not be commented");
    247         }
    248         notifyPropertyChange(ObservableProperty.COMMENT, this.comment, comment);
    249         if (this.comment != null) {
    250             this.comment.setCommentedNode(null);
    251         }
    252         this.comment = comment;
    253         if (comment != null) {
    254             this.comment.setCommentedNode(this);
    255         }
    256         return this;
    257     }
    258 
    259     /**
    260      * Use this to store additional information to this node.
    261      *
    262      * @param comment to be set
    263      */
    264     public final Node setLineComment(String comment) {
    265         return setComment(new LineComment(comment));
    266     }
    267 
    268     /**
    269      * Use this to store additional information to this node.
    270      *
    271      * @param comment to be set
    272      */
    273     public final Node setBlockComment(String comment) {
    274         return setComment(new BlockComment(comment));
    275     }
    276 
    277     /**
    278      * Return the String representation of this node.
    279      *
    280      * @return the String representation of this node
    281      */
    282     @Override
    283     public final String toString() {
    284         return toStringPrinter.print(this);
    285     }
    286 
    287     public final String toString(PrettyPrinterConfiguration prettyPrinterConfiguration) {
    288         return new PrettyPrinter(prettyPrinterConfiguration).print(this);
    289     }
    290 
    291     @Override
    292     public final int hashCode() {
    293         return HashCodeVisitor.hashCode(this);
    294     }
    295 
    296     @Override
    297     public boolean equals(final Object obj) {
    298         if (obj == null || !(obj instanceof Node)) {
    299             return false;
    300         }
    301         return EqualsVisitor.equals(this, (Node) obj);
    302     }
    303 
    304     @Override
    305     public Optional<Node> getParentNode() {
    306         return Optional.ofNullable(parentNode);
    307     }
    308 
    309     /**
    310      * Contains all nodes that have this node set as their parent.
    311      * You can add and remove nodes from this list by adding or removing nodes from the fields of this node.
    312      *
    313      * @return all nodes that have this node as their parent.
    314      */
    315     public List<Node> getChildNodes() {
    316         return unmodifiableList(childNodes);
    317     }
    318 
    319     public void addOrphanComment(Comment comment) {
    320         orphanComments.add(comment);
    321         comment.setParentNode(this);
    322     }
    323 
    324     public boolean removeOrphanComment(Comment comment) {
    325         boolean removed = orphanComments.remove(comment);
    326         if (removed) {
    327             comment.setParentNode(null);
    328         }
    329         return removed;
    330     }
    331 
    332     /**
    333      * This is a list of Comment which are inside the node and are not associated
    334      * with any meaningful AST Node.
    335      * <p>
    336      * For example, comments at the end of methods (immediately before the parenthesis)
    337      * or at the end of CompilationUnit are orphan comments.
    338      * <p>
    339      * When more than one comment preceeds a statement, the one immediately preceding it
    340      * it is associated with the statements, while the others are orphans.
    341      * <p>
    342      * Changes to this list are not persisted.
    343      *
    344      * @return all comments that cannot be attributed to a concept
    345      */
    346     public List<Comment> getOrphanComments() {
    347         return new LinkedList<>(orphanComments);
    348     }
    349 
    350     /**
    351      * This is the list of Comment which are contained in the Node either because
    352      * they are properly associated to one of its children or because they are floating
    353      * around inside the Node
    354      *
    355      * @return all Comments within the node as a list
    356      */
    357     public List<Comment> getAllContainedComments() {
    358         List<Comment> comments = new LinkedList<>();
    359         comments.addAll(getOrphanComments());
    360         for (Node child : getChildNodes()) {
    361             child.getComment().ifPresent(comments::add);
    362             comments.addAll(child.getAllContainedComments());
    363         }
    364         return comments;
    365     }
    366 
    367     /**
    368      * Assign a new parent to this node, removing it
    369      * from the list of children of the previous parent, if any.
    370      *
    371      * @param newParentNode node to be set as parent
    372      */
    373     @Override
    374     public Node setParentNode(Node newParentNode) {
    375         if (newParentNode == parentNode) {
    376             return this;
    377         }
    378         observers.forEach(o -> o.parentChange(this, parentNode, newParentNode));
    379         // remove from old parent, if any
    380         if (parentNode != null) {
    381             final List<Node> parentChildNodes = parentNode.childNodes;
    382             for (int i = 0; i < parentChildNodes.size(); i++) {
    383                 if (parentChildNodes.get(i) == this) {
    384                     parentChildNodes.remove(i);
    385                 }
    386             }
    387         }
    388         parentNode = newParentNode;
    389         // add to new parent, if any
    390         if (parentNode != null) {
    391             parentNode.childNodes.add(this);
    392         }
    393         return this;
    394     }
    395 
    396     protected void setAsParentNodeOf(Node childNode) {
    397         if (childNode != null) {
    398             childNode.setParentNode(getParentNodeForChildren());
    399         }
    400     }
    401 
    402     public static final int ABSOLUTE_BEGIN_LINE = -1;
    403 
    404     public static final int ABSOLUTE_END_LINE = -2;
    405 
    406     /**
    407      * @deprecated use getComment().isPresent()
    408      */
    409     @Deprecated
    410     public boolean hasComment() {
    411         return comment != null;
    412     }
    413 
    414     public void tryAddImportToParentCompilationUnit(Class<?> clazz) {
    415         getAncestorOfType(CompilationUnit.class).ifPresent(p -> p.addImport(clazz));
    416     }
    417 
    418     /**
    419      * Recursively finds all nodes of a certain type.
    420      *
    421      * @param clazz the type of node to find.
    422      * @deprecated use find(Class)
    423      */
    424     public <N extends Node> List<N> getChildNodesByType(Class<N> clazz) {
    425         List<N> nodes = new ArrayList<>();
    426         for (Node child : getChildNodes()) {
    427             if (clazz.isInstance(child)) {
    428                 nodes.add(clazz.cast(child));
    429             }
    430             nodes.addAll(child.getChildNodesByType(clazz));
    431         }
    432         return nodes;
    433     }
    434 
    435     /**
    436      * @deprecated use findAll(Class)
    437      */
    438     @Deprecated
    439     public <N extends Node> List<N> getNodesByType(Class<N> clazz) {
    440         return getChildNodesByType(clazz);
    441     }
    442 
    443     /**
    444      * Gets data for this node using the given key.
    445      *
    446      * @param <M> The type of the data.
    447      * @param key The key for the data
    448      * @return The data or null of no data was found for the given key
    449      * @see DataKey
    450      */
    451     @SuppressWarnings("unchecked")
    452     public <M> M getData(final DataKey<M> key) {
    453         if (data == null) {
    454             return null;
    455         }
    456         return (M) data.get(key);
    457     }
    458 
    459     /**
    460      * Sets data for this node using the given key.
    461      * For information on creating DataKey, see {@link DataKey}.
    462      *
    463      * @param <M> The type of data
    464      * @param key The singleton key for the data
    465      * @param object The data object
    466      * @see DataKey
    467      */
    468     public <M> void setData(DataKey<M> key, M object) {
    469         if (data == null) {
    470             data = new IdentityHashMap<>();
    471         }
    472         data.put(key, object);
    473     }
    474 
    475     /**
    476      * @return does this node have data for this key?
    477      */
    478     public boolean containsData(DataKey<?> key) {
    479         if (data == null) {
    480             return false;
    481         }
    482         return data.get(key) != null;
    483     }
    484 
    485     /**
    486      * Try to remove this node from the parent
    487      *
    488      * @return true if removed, false if it is a required property of the parent, or if the parent isn't set.
    489      * @throws RuntimeException if it fails in an unexpected way
    490      */
    491     public boolean remove() {
    492         if (parentNode == null) {
    493             return false;
    494         }
    495         return parentNode.remove(this);
    496     }
    497 
    498     /**
    499      * Try to replace this node in the parent with the supplied node.
    500      *
    501      * @return true if removed, or if the parent isn't set.
    502      * @throws RuntimeException if it fails in an unexpected way
    503      */
    504     public boolean replace(Node node) {
    505         if (parentNode == null) {
    506             return false;
    507         }
    508         return parentNode.replace(this, node);
    509     }
    510 
    511     /**
    512      * Forcibly removes this node from the AST.
    513      * If it cannot be removed from the parent with remove(),
    514      * it will try to remove its parent instead,
    515      * until it finds a node that can be removed,
    516      * or no parent can be found.
    517      * <p>
    518      * Since everything at CompilationUnit level is removable,
    519      * this method will only (silently) fail when the node is in a detached AST fragment.
    520      */
    521     public void removeForced() {
    522         if (!remove()) {
    523             getParentNode().ifPresent(Node::remove);
    524         }
    525     }
    526 
    527     @Override
    528     public Node getParentNodeForChildren() {
    529         return this;
    530     }
    531 
    532     protected void setAsParentNodeOf(NodeList<? extends Node> list) {
    533         if (list != null) {
    534             list.setParentNode(getParentNodeForChildren());
    535         }
    536     }
    537 
    538     public <P> void notifyPropertyChange(ObservableProperty property, P oldValue, P newValue) {
    539         this.observers.forEach(o -> o.propertyChange(this, property, oldValue, newValue));
    540     }
    541 
    542     @Override
    543     public void unregister(AstObserver observer) {
    544         this.observers.remove(observer);
    545     }
    546 
    547     @Override
    548     public void register(AstObserver observer) {
    549         this.observers.add(observer);
    550     }
    551 
    552     /**
    553      * Register a new observer for the given node. Depending on the mode specified also descendants, existing
    554      * and new, could be observed. For more details see <i>ObserverRegistrationMode</i>.
    555      */
    556     public void register(AstObserver observer, ObserverRegistrationMode mode) {
    557         if (mode == null) {
    558             throw new IllegalArgumentException("Mode should be not null");
    559         }
    560         switch(mode) {
    561             case JUST_THIS_NODE:
    562                 register(observer);
    563                 break;
    564             case THIS_NODE_AND_EXISTING_DESCENDANTS:
    565                 registerForSubtree(observer);
    566                 break;
    567             case SELF_PROPAGATING:
    568                 registerForSubtree(PropagatingAstObserver.transformInPropagatingObserver(observer));
    569                 break;
    570             default:
    571                 throw new UnsupportedOperationException("This mode is not supported: " + mode);
    572         }
    573     }
    574 
    575     /**
    576      * Register the observer for the current node and all the contained node and nodelists, recursively.
    577      */
    578     public void registerForSubtree(AstObserver observer) {
    579         register(observer);
    580         this.getChildNodes().forEach(c -> c.registerForSubtree(observer));
    581         for (PropertyMetaModel property : getMetaModel().getAllPropertyMetaModels()) {
    582             if (property.isNodeList()) {
    583                 NodeList<?> nodeList = (NodeList<?>) property.getValue(this);
    584                 if (nodeList != null)
    585                     nodeList.register(observer);
    586             }
    587         }
    588     }
    589 
    590     @Override
    591     public boolean isRegistered(AstObserver observer) {
    592         return this.observers.contains(observer);
    593     }
    594 
    595     @Generated("com.github.javaparser.generator.core.node.RemoveMethodGenerator")
    596     public boolean remove(Node node) {
    597         if (node == null)
    598             return false;
    599         if (comment != null) {
    600             if (node == comment) {
    601                 removeComment();
    602                 return true;
    603             }
    604         }
    605         return false;
    606     }
    607 
    608     @Generated("com.github.javaparser.generator.core.node.RemoveMethodGenerator")
    609     public Node removeComment() {
    610         return setComment((Comment) null);
    611     }
    612 
    613     @Override
    614     @Generated("com.github.javaparser.generator.core.node.CloneGenerator")
    615     public Node clone() {
    616         return (Node) accept(new CloneVisitor(), null);
    617     }
    618 
    619     /**
    620      * @return get JavaParser specific node introspection information.
    621      */
    622     @Generated("com.github.javaparser.generator.core.node.GetMetaModelGenerator")
    623     public NodeMetaModel getMetaModel() {
    624         return JavaParserMetaModel.nodeMetaModel;
    625     }
    626 
    627     /**
    628      * @return whether this node was successfully parsed or not.
    629      * If it was not, only the range and tokenRange fields will be valid.
    630      */
    631     public Parsedness getParsed() {
    632         return parsed;
    633     }
    634 
    635     /**
    636      * Used by the parser to flag unparsable nodes.
    637      */
    638     public Node setParsed(Parsedness parsed) {
    639         this.parsed = parsed;
    640         return this;
    641     }
    642 
    643     @Generated("com.github.javaparser.generator.core.node.ReplaceMethodGenerator")
    644     public boolean replace(Node node, Node replacementNode) {
    645         if (node == null)
    646             return false;
    647         if (comment != null) {
    648             if (node == comment) {
    649                 setComment((Comment) replacementNode);
    650                 return true;
    651             }
    652         }
    653         return false;
    654     }
    655 
    656     /**
    657      * Finds the root node of this AST by finding the topmost parent.
    658      */
    659     public Node findRootNode() {
    660         Node n = this;
    661         while (n.getParentNode().isPresent()) {
    662             n = n.getParentNode().get();
    663         }
    664         return n;
    665     }
    666 
    667     /**
    668      * @return the containing CompilationUnit, or empty if this node is not inside a compilation unit.
    669      */
    670     public Optional<CompilationUnit> findCompilationUnit() {
    671         Node rootNode = findRootNode();
    672         if (rootNode instanceof CompilationUnit) {
    673             return Optional.of((CompilationUnit) rootNode);
    674         }
    675         return Optional.empty();
    676     }
    677 
    678     protected SymbolResolver getSymbolResolver() {
    679         return findCompilationUnit().map(cu -> {
    680             SymbolResolver symbolResolver = cu.getData(SYMBOL_RESOLVER_KEY);
    681             if (symbolResolver == null) {
    682                 throw new IllegalStateException("Symbol resolution not configured: to configure consider setting a SymbolResolver in the ParserConfiguration");
    683             }
    684             return symbolResolver;
    685         }).orElseThrow(() -> new IllegalStateException("The node is not inserted in a CompilationUnit"));
    686     }
    687 
    688     // We need to expose it because we will need to use it to inject the SymbolSolver
    689     public static final DataKey<SymbolResolver> SYMBOL_RESOLVER_KEY = new DataKey<SymbolResolver>() {
    690     };
    691 
    692     public enum TreeTraversal {
    693 
    694         PREORDER, BREADTHFIRST, POSTORDER, PARENTS, DIRECT_CHILDREN
    695     }
    696 
    697     private Iterator<Node> treeIterator(TreeTraversal traversal) {
    698         switch(traversal) {
    699             case BREADTHFIRST:
    700                 return new BreadthFirstIterator(this);
    701             case POSTORDER:
    702                 return new PostOrderIterator(this);
    703             case PREORDER:
    704                 return new PreOrderIterator(this);
    705             case DIRECT_CHILDREN:
    706                 return new DirectChildrenIterator(this);
    707             case PARENTS:
    708                 return new ParentsVisitor(this);
    709             default:
    710                 throw new IllegalArgumentException("Unknown traversal choice.");
    711         }
    712     }
    713 
    714     private Iterable<Node> treeIterable(TreeTraversal traversal) {
    715         return () -> treeIterator(traversal);
    716     }
    717 
    718     /**
    719      * Make a stream of nodes using traversal algorithm "traversal".
    720      */
    721     public Stream<Node> stream(TreeTraversal traversal) {
    722         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(treeIterator(traversal), NONNULL | DISTINCT), false);
    723     }
    724 
    725     /**
    726      * Make a stream of nodes using pre-order traversal.
    727      */
    728     public Stream<Node> stream() {
    729         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(treeIterator(PREORDER), NONNULL | DISTINCT), false);
    730     }
    731 
    732     /**
    733      * Walks the AST, calling the consumer for every node, with traversal algorithm "traversal".
    734      * <br/>This is the most general walk method. All other walk and findAll methods are based on this.
    735      */
    736     public void walk(TreeTraversal traversal, Consumer<Node> consumer) {
    737         // Could be implemented as a call to the above walk method, but this is a little more efficient.
    738         for (Node node : treeIterable(traversal)) {
    739             consumer.accept(node);
    740         }
    741     }
    742 
    743     /**
    744      * Walks the AST, calling the consumer for every node with pre-order traversal.
    745      */
    746     public void walk(Consumer<Node> consumer) {
    747         walk(PREORDER, consumer);
    748     }
    749 
    750     /**
    751      * Walks the AST with pre-order traversal, calling the consumer for every node of type "nodeType".
    752      */
    753     public <T extends Node> void walk(Class<T> nodeType, Consumer<T> consumer) {
    754         walk(TreeTraversal.PREORDER, node -> {
    755             if (nodeType.isAssignableFrom(node.getClass())) {
    756                 consumer.accept(nodeType.cast(node));
    757             }
    758         });
    759     }
    760 
    761     /**
    762      * Walks the AST with pre-order traversal, returning all nodes of type "nodeType".
    763      */
    764     public <T extends Node> List<T> findAll(Class<T> nodeType) {
    765         final List<T> found = new ArrayList<>();
    766         walk(nodeType, found::add);
    767         return found;
    768     }
    769 
    770     /**
    771      * Walks the AST with pre-order traversal, returning all nodes of type "nodeType" that match the predicate.
    772      */
    773     public <T extends Node> List<T> findAll(Class<T> nodeType, Predicate<T> predicate) {
    774         final List<T> found = new ArrayList<>();
    775         walk(nodeType, n -> {
    776             if (predicate.test(n))
    777                 found.add(n);
    778         });
    779         return found;
    780     }
    781 
    782     /**
    783      * Walks the AST, applying the function for every node, with traversal algorithm "traversal". If the function
    784      * returns something else than null, the traversal is stopped and the function result is returned. <br/>This is the
    785      * most general findFirst method. All other findFirst methods are based on this.
    786      */
    787     public <T> Optional<T> findFirst(TreeTraversal traversal, Function<Node, Optional<T>> consumer) {
    788         for (Node node : treeIterable(traversal)) {
    789             final Optional<T> result = consumer.apply(node);
    790             if (result.isPresent()) {
    791                 return result;
    792             }
    793         }
    794         return Optional.empty();
    795     }
    796 
    797     /**
    798      * Walks the AST with pre-order traversal, returning the first node of type "nodeType" or empty() if none is found.
    799      */
    800     public <N extends Node> Optional<N> findFirst(Class<N> nodeType) {
    801         return findFirst(TreeTraversal.PREORDER, node -> {
    802             if (nodeType.isAssignableFrom(node.getClass())) {
    803                 return Optional.of(nodeType.cast(node));
    804             }
    805             return Optional.empty();
    806         });
    807     }
    808 
    809     /**
    810      * Walks the AST with pre-order traversal, returning the first node of type "nodeType" that matches "predicate" or empty() if none is
    811      * found.
    812      */
    813     public <N extends Node> Optional<N> findFirst(Class<N> nodeType, Predicate<N> predicate) {
    814         return findFirst(TreeTraversal.PREORDER, node -> {
    815             if (nodeType.isAssignableFrom(node.getClass())) {
    816                 final N castNode = nodeType.cast(node);
    817                 if (predicate.test(castNode)) {
    818                     return Optional.of(castNode);
    819                 }
    820             }
    821             return Optional.empty();
    822         });
    823     }
    824 
    825     /**
    826      * Walks the parents of this node, returning the first node of type "nodeType" or empty() if none is found.
    827      */
    828     public <N extends Node> Optional<N> findParent(Class<N> nodeType) {
    829         Node n = this;
    830         while (n.getParentNode().isPresent()) {
    831             n = n.getParentNode().get();
    832             if (nodeType.isAssignableFrom(n.getClass())) {
    833                 return Optional.of(nodeType.cast(n));
    834             }
    835         }
    836         return Optional.empty();
    837     }
    838 
    839     /**
    840      * Performs a breadth-first node traversal starting with a given node.
    841      *
    842      * @see <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Breadth-first traversal</a>
    843      */
    844     public static class BreadthFirstIterator implements Iterator<Node> {
    845 
    846         private final Queue<Node> queue = new LinkedList<>();
    847 
    848         public BreadthFirstIterator(Node node) {
    849             queue.add(node);
    850         }
    851 
    852         @Override
    853         public boolean hasNext() {
    854             return !queue.isEmpty();
    855         }
    856 
    857         @Override
    858         public Node next() {
    859             Node next = queue.remove();
    860             queue.addAll(next.getChildNodes());
    861             return next;
    862         }
    863     }
    864 
    865     /**
    866      * Performs a simple traversal over all nodes that have the passed node as their parent.
    867      */
    868     public static class DirectChildrenIterator implements Iterator<Node> {
    869 
    870         private final Iterator<Node> childrenIterator;
    871 
    872         public DirectChildrenIterator(Node node) {
    873             childrenIterator = new ArrayList<>(node.getChildNodes()).iterator();
    874         }
    875 
    876         @Override
    877         public boolean hasNext() {
    878             return childrenIterator.hasNext();
    879         }
    880 
    881         @Override
    882         public Node next() {
    883             return childrenIterator.next();
    884         }
    885     }
    886 
    887     /**
    888      * Iterates over the parent of the node, then the parent's parent, then the parent's parent's parent, until running
    889      * out of parents.
    890      */
    891     public static class ParentsVisitor implements Iterator<Node> {
    892 
    893         private Node node;
    894 
    895         public ParentsVisitor(Node node) {
    896             this.node = node;
    897         }
    898 
    899         @Override
    900         public boolean hasNext() {
    901             return node.getParentNode().isPresent();
    902         }
    903 
    904         @Override
    905         public Node next() {
    906             node = node.getParentNode().orElse(null);
    907             return node;
    908         }
    909     }
    910 
    911     /**
    912      * Performs a pre-order (or depth-first) node traversal starting with a given node.
    913      *
    914      * @see <a href="https://en.wikipedia.org/wiki/Pre-order">Pre-order traversal</a>
    915      */
    916     public static class PreOrderIterator implements Iterator<Node> {
    917 
    918         private final Stack<Node> stack = new Stack<>();
    919 
    920         public PreOrderIterator(Node node) {
    921             stack.add(node);
    922         }
    923 
    924         @Override
    925         public boolean hasNext() {
    926             return !stack.isEmpty();
    927         }
    928 
    929         @Override
    930         public Node next() {
    931             Node next = stack.pop();
    932             List<Node> children = next.getChildNodes();
    933             for (int i = children.size() - 1; i >= 0; i--) {
    934                 stack.add(children.get(i));
    935             }
    936             return next;
    937         }
    938     }
    939 
    940     /**
    941      * Performs a post-order (or leaves-first) node traversal starting with a given node.
    942      *
    943      * @see <a href="https://en.wikipedia.org/wiki/Post-order">Post-order traversal</a>
    944      */
    945     public static class PostOrderIterator implements Iterator<Node> {
    946 
    947         private final Stack<List<Node>> nodesStack = new Stack<>();
    948 
    949         private final Stack<Integer> cursorStack = new Stack<>();
    950 
    951         private final Node root;
    952 
    953         private boolean hasNext = true;
    954 
    955         public PostOrderIterator(Node root) {
    956             this.root = root;
    957             fillStackToLeaf(root);
    958         }
    959 
    960         private void fillStackToLeaf(Node node) {
    961             while (true) {
    962                 List<Node> childNodes = new ArrayList<>(node.getChildNodes());
    963                 if (childNodes.isEmpty()) {
    964                     break;
    965                 }
    966                 nodesStack.push(childNodes);
    967                 cursorStack.push(0);
    968                 node = childNodes.get(0);
    969             }
    970         }
    971 
    972         @Override
    973         public boolean hasNext() {
    974             return hasNext;
    975         }
    976 
    977         @Override
    978         public Node next() {
    979             final List<Node> nodes = nodesStack.peek();
    980             final int cursor = cursorStack.peek();
    981             final boolean levelHasNext = cursor < nodes.size();
    982             if (levelHasNext) {
    983                 Node node = nodes.get(cursor);
    984                 fillStackToLeaf(node);
    985                 return nextFromLevel();
    986             } else {
    987                 nodesStack.pop();
    988                 cursorStack.pop();
    989                 hasNext = !nodesStack.empty();
    990                 if (hasNext) {
    991                     return nextFromLevel();
    992                 }
    993                 return root;
    994             }
    995         }
    996 
    997         private Node nextFromLevel() {
    998             final List<Node> nodes = nodesStack.peek();
    999             final int cursor = cursorStack.pop();
   1000             cursorStack.push(cursor + 1);
   1001             return nodes.get(cursor);
   1002         }
   1003     }
   1004 }
   1005