TreePath.java revision 3193:3b3bea483542
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
26package com.sun.source.util;
27
28import java.util.Iterator;
29import java.util.Objects;
30
31import 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 */
40public 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     */
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     */
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     */
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     */
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     */
120    public CompilationUnitTree getCompilationUnit() {
121        return compilationUnit;
122    }
123
124    /**
125     * Returns the leaf node for this path.
126     * @return the leaf node
127     */
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     */
136    public TreePath getParentPath() {
137        return parent;
138    }
139
140    /**
141     *  Iterates from leaves to root.
142     */
143    @Override
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