001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.utils;
021
022import antlr.collections.AST;
023
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.Scope;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027
028/**
029 * Contains utility methods for working on scope.
030 *
031 * @author Oliver Burn
032 */
033public final class ScopeUtils {
034    /** Prevent instantiation. */
035    private ScopeUtils() {
036    }
037
038    /**
039     * Returns the Scope specified by the modifier set.
040     *
041     * @param aMods root node of a modifier set
042     * @return a {@code Scope} value
043     */
044    public static Scope getScopeFromMods(DetailAST aMods) {
045        // default scope
046        Scope returnValue = Scope.PACKAGE;
047        for (AST token = aMods.getFirstChild(); token != null
048                && returnValue == Scope.PACKAGE;
049                token = token.getNextSibling()) {
050            if ("public".equals(token.getText())) {
051                returnValue = Scope.PUBLIC;
052            }
053            else if ("protected".equals(token.getText())) {
054                returnValue = Scope.PROTECTED;
055            }
056            else if ("private".equals(token.getText())) {
057                returnValue = Scope.PRIVATE;
058            }
059        }
060        return returnValue;
061    }
062
063    /**
064     * Returns the scope of the surrounding "block".
065     * @param aAST the node to return the scope for
066     * @return the Scope of the surrounding block
067     */
068    public static Scope getSurroundingScope(DetailAST aAST) {
069        Scope returnValue = null;
070        for (DetailAST token = aAST.getParent();
071             token != null;
072             token = token.getParent()) {
073            final int type = token.getType();
074            if (type == TokenTypes.CLASS_DEF
075                || type == TokenTypes.INTERFACE_DEF
076                || type == TokenTypes.ANNOTATION_DEF
077                || type == TokenTypes.ENUM_DEF) {
078                final DetailAST mods =
079                    token.findFirstToken(TokenTypes.MODIFIERS);
080                final Scope modScope = getScopeFromMods(mods);
081                if (returnValue == null || returnValue.isIn(modScope)) {
082                    returnValue = modScope;
083                }
084            }
085            else if (type == TokenTypes.LITERAL_NEW) {
086                returnValue = Scope.ANONINNER;
087                // because Scope.ANONINNER is not in any other Scope
088                break;
089            }
090        }
091
092        return returnValue;
093    }
094
095    /**
096     * Returns whether a node is directly contained within an interface block.
097     *
098     * @param aAST the node to check if directly contained within an interface block.
099     * @return a {@code boolean} value
100     */
101    public static boolean isInInterfaceBlock(DetailAST aAST) {
102        boolean returnValue = false;
103
104        // Loop up looking for a containing interface block
105        for (DetailAST token = aAST.getParent();
106             token != null && !returnValue;
107             token = token.getParent()) {
108
109            final int type = token.getType();
110
111            if (type == TokenTypes.INTERFACE_DEF) {
112                returnValue = true;
113            }
114            else if (type == TokenTypes.CLASS_DEF
115                || type == TokenTypes.ENUM_DEF
116                || type == TokenTypes.ANNOTATION_DEF
117                || type == TokenTypes.LITERAL_NEW) {
118                break;
119            }
120        }
121
122        return returnValue;
123    }
124
125    /**
126     * Returns whether a node is directly contained within an annotation block.
127     *
128     * @param aAST the node to check if directly contained within an annotation block.
129     * @return a {@code boolean} value
130     */
131    public static boolean isInAnnotationBlock(DetailAST aAST) {
132        boolean returnValue = false;
133
134        // Loop up looking for a containing interface block
135        for (DetailAST token = aAST.getParent();
136             token != null && !returnValue;
137             token = token.getParent()) {
138            final int type = token.getType();
139            if (type == TokenTypes.ANNOTATION_DEF) {
140                returnValue = true;
141            }
142            else if (type == TokenTypes.CLASS_DEF
143                || type == TokenTypes.ENUM_DEF
144                || type == TokenTypes.INTERFACE_DEF
145                || type == TokenTypes.LITERAL_NEW) {
146                break;
147            }
148
149        }
150
151        return returnValue;
152    }
153
154    /**
155     * Returns whether a node is directly contained within an interface or
156     * annotation block.
157     *
158     * @param aAST the node to check if directly contained within an interface
159     *     or annotation block.
160     * @return a {@code boolean} value
161     */
162    public static boolean isInInterfaceOrAnnotationBlock(DetailAST aAST) {
163        return isInInterfaceBlock(aAST) || isInAnnotationBlock(aAST);
164    }
165
166    /**
167     * Returns whether a node is directly contained within an enum block.
168     *
169     * @param aAST the node to check if directly contained within an enum block.
170     * @return a {@code boolean} value
171     */
172    public static boolean isInEnumBlock(DetailAST aAST) {
173        boolean returnValue = false;
174
175        // Loop up looking for a containing interface block
176        for (DetailAST token = aAST.getParent();
177             token != null && !returnValue;
178             token = token.getParent()) {
179            final int type = token.getType();
180            if (type == TokenTypes.ENUM_DEF) {
181                returnValue = true;
182            }
183            else if (type == TokenTypes.INTERFACE_DEF
184                || type == TokenTypes.ANNOTATION_DEF
185                || type == TokenTypes.CLASS_DEF
186                || type == TokenTypes.LITERAL_NEW) {
187                break;
188            }
189        }
190
191        return returnValue;
192    }
193
194    /**
195     * Returns whether the scope of a node is restricted to a code block.
196     * A code block is a method or constructor body, or a initializer block.
197     *
198     * @param aAST the node to check
199     * @return a {@code boolean} value
200     */
201    public static boolean isInCodeBlock(DetailAST aAST) {
202        boolean returnValue = false;
203
204        // Loop up looking for a containing code block
205        for (DetailAST token = aAST.getParent();
206             token != null;
207             token = token.getParent()) {
208            final int type = token.getType();
209            if (type == TokenTypes.METHOD_DEF
210                || type == TokenTypes.CTOR_DEF
211                || type == TokenTypes.INSTANCE_INIT
212                || type == TokenTypes.STATIC_INIT) {
213                returnValue = true;
214                break;
215            }
216        }
217
218        return returnValue;
219    }
220
221    /**
222     * Returns whether a node is contained in the outer most type block.
223     *
224     * @param aAST the node to check
225     * @return a {@code boolean} value
226     */
227    public static boolean isOuterMostType(DetailAST aAST) {
228        boolean returnValue = true;
229        for (DetailAST parent = aAST.getParent();
230             parent != null;
231             parent = parent.getParent()) {
232            if (parent.getType() == TokenTypes.CLASS_DEF
233                || parent.getType() == TokenTypes.INTERFACE_DEF
234                || parent.getType() == TokenTypes.ANNOTATION_DEF
235                || parent.getType() == TokenTypes.ENUM_DEF) {
236                returnValue = false;
237                break;
238            }
239        }
240
241        return returnValue;
242    }
243
244    /**
245     * Determines whether a node is a local variable definition.
246     * I.e. if it is declared in a code block, a for initializer,
247     * or a catch parameter.
248     * @param aAST the node to check.
249     * @return whether aAST is a local variable definition.
250     */
251    public static boolean isLocalVariableDef(DetailAST aAST) {
252        boolean localVariableDef = false;
253        // variable declaration?
254        if (aAST.getType() == TokenTypes.VARIABLE_DEF) {
255            final DetailAST parent = aAST.getParent();
256            final int type = parent.getType();
257            localVariableDef = type == TokenTypes.SLIST
258                    || type == TokenTypes.FOR_INIT
259                    || type == TokenTypes.FOR_EACH_CLAUSE;
260        }
261        // catch parameter?
262        if (aAST.getType() == TokenTypes.PARAMETER_DEF) {
263            final DetailAST parent = aAST.getParent();
264            localVariableDef = parent.getType() == TokenTypes.LITERAL_CATCH;
265        }
266        return localVariableDef;
267    }
268
269    /**
270     * Checks whether ast node is in a specific scope.
271     * @param ast the node to check.
272     * @param scope a {@code Scope} value.
273     * @return true if the ast node is in the scope.
274     */
275    public static boolean isInScope(DetailAST ast, Scope scope) {
276        final Scope surroundingScopeOfAstToken = getSurroundingScope(ast);
277        return surroundingScopeOfAstToken == scope;
278    }
279}