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