001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.Scope;
024import com.puppycrawl.tools.checkstyle.api.TokenTypes;
025
026/**
027 * Contains utility methods for working on scope.
028 *
029 */
030public final class ScopeUtil {
031
032    /** Prevent instantiation. */
033    private ScopeUtil() {
034    }
035
036    /**
037     * Returns the Scope specified by the modifier set.
038     *
039     * @param aMods root node of a modifier set
040     * @return a {@code Scope} value
041     */
042    public static Scope getScopeFromMods(DetailAST aMods) {
043        // default scope
044        Scope returnValue = Scope.PACKAGE;
045        for (DetailAST token = aMods.getFirstChild(); token != null
046                && returnValue == Scope.PACKAGE;
047                token = token.getNextSibling()) {
048            if ("public".equals(token.getText())) {
049                returnValue = Scope.PUBLIC;
050            }
051            else if ("protected".equals(token.getText())) {
052                returnValue = Scope.PROTECTED;
053            }
054            else if ("private".equals(token.getText())) {
055                returnValue = Scope.PRIVATE;
056            }
057        }
058        return returnValue;
059    }
060
061    /**
062     * Returns the scope of the surrounding "block".
063     *
064     * @param node the node to return the scope for
065     * @return the Scope of the surrounding block
066     */
067    public static Scope getSurroundingScope(DetailAST node) {
068        Scope returnValue = null;
069        for (DetailAST token = node.getParent();
070             token != null;
071             token = token.getParent()) {
072            final int type = token.getType();
073            if (TokenUtil.isTypeDeclaration(type)) {
074                final DetailAST mods =
075                    token.findFirstToken(TokenTypes.MODIFIERS);
076                final Scope modScope = getScopeFromMods(mods);
077                if (returnValue == null || returnValue.isIn(modScope)) {
078                    returnValue = modScope;
079                }
080            }
081            else if (type == TokenTypes.LITERAL_NEW) {
082                returnValue = Scope.ANONINNER;
083                // because Scope.ANONINNER is not in any other Scope
084                break;
085            }
086        }
087
088        return returnValue;
089    }
090
091    /**
092     * Returns whether a node is directly contained within a class block.
093     *
094     * @param node the node to check if directly contained within a class block.
095     * @return a {@code boolean} value
096     */
097    public static boolean isInClassBlock(DetailAST node) {
098        return isInBlockOf(node, TokenTypes.CLASS_DEF);
099    }
100
101    /**
102     * Returns whether a node is directly contained within an interface block.
103     *
104     * @param node the node to check if directly contained within an interface block.
105     * @return a {@code boolean} value
106     */
107    public static boolean isInInterfaceBlock(DetailAST node) {
108        return isInBlockOf(node, TokenTypes.INTERFACE_DEF);
109    }
110
111    /**
112     * Returns whether a node is directly contained within an annotation block.
113     *
114     * @param node the node to check if directly contained within an annotation block.
115     * @return a {@code boolean} value
116     */
117    public static boolean isInAnnotationBlock(DetailAST node) {
118        return isInBlockOf(node, TokenTypes.ANNOTATION_DEF);
119    }
120
121    /**
122     * Returns whether a node is directly contained within a specified block.
123     *
124     * @param node the node to check if directly contained within a specified block.
125     * @param tokenType type of token.
126     * @return a {@code boolean} value
127     */
128    private static boolean isInBlockOf(DetailAST node, int tokenType) {
129        boolean returnValue = false;
130
131        // Loop up looking for a containing interface block
132        for (DetailAST token = node.getParent();
133             token != null && !returnValue;
134             token = token.getParent()) {
135            final int type = token.getType();
136            if (type == tokenType) {
137                returnValue = true;
138            }
139            else if (type == TokenTypes.LITERAL_NEW || TokenUtil.isTypeDeclaration(type)) {
140                break;
141            }
142        }
143
144        return returnValue;
145    }
146
147    /**
148     * Returns whether a node is directly contained within an interface or
149     * annotation block.
150     *
151     * @param node the node to check if directly contained within an interface
152     *     or annotation block.
153     * @return a {@code boolean} value
154     */
155    public static boolean isInInterfaceOrAnnotationBlock(DetailAST node) {
156        return isInInterfaceBlock(node) || isInAnnotationBlock(node);
157    }
158
159    /**
160     * Returns whether a node is directly contained within an enum block.
161     *
162     * @param node the node to check if directly contained within an enum block.
163     * @return a {@code boolean} value
164     */
165    public static boolean isInEnumBlock(DetailAST node) {
166        boolean returnValue = false;
167
168        // Loop up looking for a containing interface block
169        for (DetailAST token = node.getParent();
170             token != null && !returnValue;
171             token = token.getParent()) {
172            final int type = token.getType();
173            if (type == TokenTypes.ENUM_DEF) {
174                returnValue = true;
175            }
176            else if (type == TokenTypes.INTERFACE_DEF
177                || type == TokenTypes.ANNOTATION_DEF
178                || type == TokenTypes.CLASS_DEF
179                || type == TokenTypes.LITERAL_NEW) {
180                break;
181            }
182        }
183
184        return returnValue;
185    }
186
187    /**
188     * Returns whether the scope of a node is restricted to a code block.
189     * A code block is a method or constructor body, an initializer block, or lambda body.
190     *
191     * @param node the node to check
192     * @return a {@code boolean} value
193     */
194    public static boolean isInCodeBlock(DetailAST node) {
195        boolean returnValue = false;
196
197        // Loop up looking for a containing code block
198        for (DetailAST token = node.getParent();
199             token != null;
200             token = token.getParent()) {
201            final int type = token.getType();
202            if (type == TokenTypes.METHOD_DEF
203                    || type == TokenTypes.CTOR_DEF
204                    || type == TokenTypes.INSTANCE_INIT
205                    || type == TokenTypes.STATIC_INIT
206                    || type == TokenTypes.LAMBDA) {
207                returnValue = true;
208                break;
209            }
210        }
211
212        return returnValue;
213    }
214
215    /**
216     * Returns whether a node is contained in the outer most type block.
217     *
218     * @param node the node to check
219     * @return a {@code boolean} value
220     */
221    public static boolean isOuterMostType(DetailAST node) {
222        boolean returnValue = true;
223        for (DetailAST parent = node.getParent();
224             parent != null;
225             parent = parent.getParent()) {
226            if (TokenUtil.isTypeDeclaration(parent.getType())) {
227                returnValue = false;
228                break;
229            }
230        }
231
232        return returnValue;
233    }
234
235    /**
236     * Determines whether a node is a local variable definition.
237     * I.e. if it is declared in a code block, a for initializer,
238     * or a catch parameter.
239     *
240     * @param node the node to check.
241     * @return whether aAST is a local variable definition.
242     */
243    public static boolean isLocalVariableDef(DetailAST node) {
244        boolean localVariableDef = false;
245        // variable declaration?
246        if (node.getType() == TokenTypes.VARIABLE_DEF) {
247            final DetailAST parent = node.getParent();
248            final int type = parent.getType();
249            localVariableDef = type == TokenTypes.SLIST
250                    || type == TokenTypes.FOR_INIT
251                    || type == TokenTypes.FOR_EACH_CLAUSE;
252        }
253        // catch parameter?
254        if (node.getType() == TokenTypes.PARAMETER_DEF) {
255            final DetailAST parent = node.getParent();
256            localVariableDef = parent.getType() == TokenTypes.LITERAL_CATCH;
257        }
258
259        if (node.getType() == TokenTypes.RESOURCE) {
260            localVariableDef = true;
261        }
262        return localVariableDef;
263    }
264
265    /**
266     * Determines whether a node is a class field definition.
267     * I.e. if a variable is not declared in a code block, a for initializer,
268     * or a catch parameter.
269     *
270     * @param node the node to check.
271     * @return whether a node is a class field definition.
272     */
273    public static boolean isClassFieldDef(DetailAST node) {
274        return node.getType() == TokenTypes.VARIABLE_DEF && !isLocalVariableDef(node);
275    }
276
277    /**
278     * Checks whether ast node is in a specific scope.
279     *
280     * @param ast the node to check.
281     * @param scope a {@code Scope} value.
282     * @return true if the ast node is in the scope.
283     */
284    public static boolean isInScope(DetailAST ast, Scope scope) {
285        final Scope surroundingScopeOfAstToken = getSurroundingScope(ast);
286        return surroundingScopeOfAstToken == scope;
287    }
288
289}