1 /*
2  * Copyright (c) 2006, 2014, 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 com.sun.source.util;
27 
28 import java.util.Iterator;
29 import java.util.Objects;
30 
31 import com.sun.source.tree.*;
32 
33 /**
34  * A path of tree nodes, typically used to represent the sequence of ancestor
35  * nodes of a tree node up to the top level CompilationUnitTree node.
36  *
37  * @author Jonathan Gibbons
38  * @since 1.6
39  */
40 public class TreePath implements Iterable<Tree> {
41     /**
42      * Returns a tree path for a tree node within a compilation unit,
43      * or {@code null} if the node is not found.
44      * @param unit the compilation unit to search
45      * @param target the node to locate
46      * @return the tree path
47      */
getPath(CompilationUnitTree unit, Tree target)48     public static TreePath getPath(CompilationUnitTree unit, Tree target) {
49         return getPath(new TreePath(unit), target);
50     }
51 
52     /**
53      * Returns a tree path for a tree node within a subtree identified by a TreePath object.
54      * Returns {@code null} if the node is not found.
55      * @param path the path in which to search
56      * @param target the node to locate
57      * @return the tree path of the target node
58      */
getPath(TreePath path, Tree target)59     public static TreePath getPath(TreePath path, Tree target) {
60         Objects.requireNonNull(path);
61         Objects.requireNonNull(target);
62 
63         class Result extends Error {
64             static final long serialVersionUID = -5942088234594905625L;
65             TreePath path;
66             Result(TreePath path) {
67                 this.path = path;
68             }
69         }
70 
71         class PathFinder extends TreePathScanner<TreePath,Tree> {
72             public TreePath scan(Tree tree, Tree target) {
73                 if (tree == target) {
74                     throw new Result(new TreePath(getCurrentPath(), target));
75                 }
76                 return super.scan(tree, target);
77             }
78         }
79 
80         if (path.getLeaf() == target) {
81             return path;
82         }
83 
84         try {
85             new PathFinder().scan(path, target);
86         } catch (Result result) {
87             return result.path;
88         }
89         return null;
90     }
91 
92     /**
93      * Creates a TreePath for a root node.
94      * @param node the root node
95      */
TreePath(CompilationUnitTree node)96     public TreePath(CompilationUnitTree node) {
97         this(null, node);
98     }
99 
100     /**
101      * Creates a TreePath for a child node.
102      * @param path the parent path
103      * @param tree the child node
104      */
TreePath(TreePath path, Tree tree)105     public TreePath(TreePath path, Tree tree) {
106         if (tree.getKind() == Tree.Kind.COMPILATION_UNIT) {
107             compilationUnit = (CompilationUnitTree) tree;
108             parent = null;
109         }
110         else {
111             compilationUnit = path.compilationUnit;
112             parent = path;
113         }
114         leaf = tree;
115     }
116     /**
117      * Returns the compilation unit associated with this path.
118      * @return the compilation unit
119      */
getCompilationUnit()120     public CompilationUnitTree getCompilationUnit() {
121         return compilationUnit;
122     }
123 
124     /**
125      * Returns the leaf node for this path.
126      * @return the leaf node
127      */
getLeaf()128     public Tree getLeaf() {
129         return leaf;
130     }
131 
132     /**
133      * Returns the path for the enclosing node, or {@code null} if there is no enclosing node.
134      * @return the path for the enclosing node
135      */
getParentPath()136     public TreePath getParentPath() {
137         return parent;
138     }
139 
140     /**
141      *  Iterates from leaves to root.
142      */
143     @Override
iterator()144     public Iterator<Tree> iterator() {
145         return new Iterator<Tree>() {
146             @Override
147             public boolean hasNext() {
148                 return next != null;
149             }
150 
151             @Override
152             public Tree next() {
153                 Tree t = next.leaf;
154                 next = next.parent;
155                 return t;
156             }
157 
158             @Override
159             public void remove() {
160                 throw new UnsupportedOperationException();
161             }
162 
163             private TreePath next = TreePath.this;
164         };
165     }
166 
167     private CompilationUnitTree compilationUnit;
168     private Tree leaf;
169     private TreePath parent;
170 }
171