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.checks;
020
021import com.google.common.collect.Lists;
022import com.google.common.collect.Maps;
023import com.google.common.collect.Sets;
024import com.puppycrawl.tools.checkstyle.api.Check;
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
027import com.puppycrawl.tools.checkstyle.api.TokenTypes;
028
029import java.util.Deque;
030import java.util.Map;
031import java.util.Queue;
032import java.util.Set;
033
034/**
035 * Abstract class for checks which need to collect information about
036 * declared members/parameters/variables.
037 *
038 * @author o_sukhodolsky
039 */
040public abstract class DeclarationCollector extends Check
041{
042    /**
043     * Tree of all the parsed frames
044     */
045    private Map<DetailAST, LexicalFrame> frames;
046
047    /**
048     * Frame for the currently processed AST
049     */
050    private LexicalFrame current;
051
052    @Override
053    public void beginTree(DetailAST rootAST)
054    {
055        final Deque<LexicalFrame> frameStack = Lists.newLinkedList();
056        frameStack.add(new GlobalFrame());
057
058        frames = Maps.newHashMap();
059
060        DetailAST curNode = rootAST;
061        while (curNode != null) {
062            collectDeclarations(frameStack, curNode);
063            DetailAST toVisit = curNode.getFirstChild();
064            while (curNode != null && toVisit == null) {
065                endCollectingDeclarations(frameStack, curNode);
066                toVisit = curNode.getNextSibling();
067                if (toVisit == null) {
068                    curNode = curNode.getParent();
069                }
070            }
071            curNode = toVisit;
072        }
073    }
074
075    @Override
076    public void visitToken(DetailAST ast)
077    {
078        switch (ast.getType()) {
079            case TokenTypes.CLASS_DEF :
080            case TokenTypes.INTERFACE_DEF :
081            case TokenTypes.ENUM_DEF :
082            case TokenTypes.ANNOTATION_DEF :
083            case TokenTypes.SLIST :
084            case TokenTypes.METHOD_DEF :
085            case TokenTypes.CTOR_DEF :
086                this.current = this.frames.get(ast);
087                break;
088            default :
089                // do nothing
090        }
091    } // end visitToken
092
093    /**
094     * Parse the next AST for declarations
095     *
096     * @param frameStack Stack containing the FrameTree being built
097     * @param ast AST to parse
098     */
099    private void collectDeclarations(Deque<LexicalFrame> frameStack,
100        DetailAST ast)
101    {
102        final LexicalFrame frame = frameStack.peek();
103        switch (ast.getType()) {
104            case TokenTypes.VARIABLE_DEF :  {
105                final String name =
106                        ast.findFirstToken(TokenTypes.IDENT).getText();
107                if (frame instanceof ClassFrame) {
108                    final DetailAST mods =
109                            ast.findFirstToken(TokenTypes.MODIFIERS);
110                    if (ScopeUtils.inInterfaceBlock(ast)
111                            || mods.branchContains(TokenTypes.LITERAL_STATIC))
112                    {
113                        ((ClassFrame) frame).addStaticMember(name);
114                    }
115                    else {
116                        ((ClassFrame) frame).addInstanceMember(name);
117                    }
118                }
119                else {
120                    frame.addName(name);
121                }
122                break;
123            }
124            case TokenTypes.PARAMETER_DEF : {
125                final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
126                frame.addName(nameAST.getText());
127                break;
128            }
129            case TokenTypes.CLASS_DEF :
130            case TokenTypes.INTERFACE_DEF :
131            case TokenTypes.ENUM_DEF :
132            case TokenTypes.ANNOTATION_DEF : {
133                final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
134                frame.addName(nameAST.getText());
135                frameStack.addFirst(new ClassFrame(frame));
136                break;
137            }
138            case TokenTypes.SLIST :
139                frameStack.addFirst(new BlockFrame(frame));
140                break;
141            case TokenTypes.METHOD_DEF : {
142                final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
143                if (frame instanceof ClassFrame) {
144                    final DetailAST mods =
145                            ast.findFirstToken(TokenTypes.MODIFIERS);
146                    if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
147                        ((ClassFrame) frame).addStaticMethod(name);
148                    }
149                    else {
150                        ((ClassFrame) frame).addInstanceMethod(name);
151                    }
152                }
153            }
154            case TokenTypes.CTOR_DEF :
155                frameStack.addFirst(new MethodFrame(frame));
156                break;
157            default:
158                // do nothing
159        }
160    }
161
162
163    /**
164     * End parsing of the AST for declarations.
165     *
166     * @param frameStack Stack containing the FrameTree being built
167     * @param ast AST that was parsed
168     */
169    private void endCollectingDeclarations(Queue<LexicalFrame> frameStack,
170        DetailAST ast)
171    {
172        switch (ast.getType()) {
173            case TokenTypes.CLASS_DEF :
174            case TokenTypes.INTERFACE_DEF :
175            case TokenTypes.ENUM_DEF :
176            case TokenTypes.ANNOTATION_DEF :
177            case TokenTypes.SLIST :
178            case TokenTypes.METHOD_DEF :
179            case TokenTypes.CTOR_DEF :
180                this.frames.put(ast, frameStack.poll());
181                break;
182            default :
183                // do nothing
184        }
185    }
186
187    /**
188     * Check if given name is a name for class field in current environment.
189     * @param name a name to check
190     * @return true is the given name is name of member.
191     */
192    protected final boolean isClassField(String name)
193    {
194        final LexicalFrame frame = findFrame(name);
195        return (frame instanceof ClassFrame)
196                && ((ClassFrame) frame).hasInstanceMember(name);
197    }
198
199    /**
200     * Check if given name is a name for class method in current environment.
201     * @param name a name to check
202     * @return true is the given name is name of method.
203     */
204    protected final boolean isClassMethod(String name)
205    {
206        final LexicalFrame frame = findFrame(name);
207        return (frame instanceof ClassFrame)
208                && ((ClassFrame) frame).hasInstanceMethod(name);
209    }
210
211    /**
212     * Find frame containing declaration
213     * @param name name of the declaration to find
214     * @return LexicalFrame containing declaration or null
215     */
216    private LexicalFrame findFrame(String name)
217    {
218        if (current != null) {
219            return current.getIfContains(name);
220        }
221        else {
222            return null;
223        }
224    }
225
226    /**
227     * A declaration frame.
228     * @author Stephen Bloch
229     * June 19, 2003
230     */
231    private abstract static class LexicalFrame
232    {
233        /** Set of name of variables declared in this frame. */
234        private final Set<String> varNames;
235        /**
236         * Parent frame.
237         */
238        private final LexicalFrame parent;
239
240        /**
241         * constructor -- invokable only via super() from subclasses
242         *
243         * @param parent parent frame
244         */
245        protected LexicalFrame(LexicalFrame parent)
246        {
247            this.parent = parent;
248            varNames = Sets.newHashSet();
249        }
250
251        /** add a name to the frame.
252         * @param nameToAdd  the name we're adding
253         */
254        void addName(String nameToAdd)
255        {
256            varNames.add(nameToAdd);
257        }
258
259        /** check whether the frame contains a given name.
260         * @param nameToFind  the name we're looking for
261         * @return whether it was found
262         */
263        boolean contains(String nameToFind)
264        {
265            return varNames.contains(nameToFind);
266        }
267
268        /** check whether the frame contains a given name.
269         * @param nameToFind  the name we're looking for
270         * @return whether it was found
271         */
272        LexicalFrame getIfContains(String nameToFind)
273        {
274            if (contains(nameToFind)) {
275                return this;
276            }
277            else if (parent != null) {
278                return parent.getIfContains(nameToFind);
279            }
280            else {
281                return null;
282            }
283        }
284    }
285
286    /**
287     * The global frame; should hold only class names.
288     * @author Stephen Bloch
289     */
290    private static class GlobalFrame extends LexicalFrame
291    {
292
293        /**
294         * Constructor for the root of the FrameTree
295         */
296        protected GlobalFrame()
297        {
298            super(null);
299        }
300    }
301
302    /**
303     * A frame initiated at method definition; holds parameter names.
304     * @author Stephen Bloch
305     */
306    private static class MethodFrame extends LexicalFrame
307    {
308        /**
309         * @param parent parent frame
310         */
311        protected MethodFrame(LexicalFrame parent)
312        {
313            super(parent);
314        }
315    }
316
317    /**
318     * A frame initiated at class definition; holds instance variable
319     * names.  For the present, I'm not worried about other class names,
320     * method names, etc.
321     * @author Stephen Bloch
322     */
323    private static class ClassFrame extends LexicalFrame
324    {
325        /** Set of name of instance members declared in this frame. */
326        private final Set<String> instanceMembers;
327        /** Set of name of instance methods declared in this frame. */
328        private final Set<String> instanceMethods;
329        /** Set of name of variables declared in this frame. */
330        private final Set<String> staticMembers;
331        /** Set of name of static methods declared in this frame. */
332        private final Set<String> staticMethods;
333
334        /**
335         * Creates new instance of ClassFrame
336         * @param parent parent frame
337         */
338        public ClassFrame(LexicalFrame parent)
339        {
340            super(parent);
341            instanceMembers = Sets.newHashSet();
342            instanceMethods = Sets.newHashSet();
343            staticMembers = Sets.newHashSet();
344            staticMethods = Sets.newHashSet();
345        }
346
347        /**
348         * Adds static member's name.
349         * @param name a name of static member of the class
350         */
351        public void addStaticMember(final String name)
352        {
353            staticMembers.add(name);
354        }
355
356        /**
357         * Adds static method's name.
358         * @param name a name of static method of the class
359         */
360        public void addStaticMethod(final String name)
361        {
362            staticMethods.add(name);
363        }
364
365        /**
366         * Adds instance member's name.
367         * @param name a name of instance member of the class
368         */
369        public void addInstanceMember(final String name)
370        {
371            instanceMembers.add(name);
372        }
373
374        /**
375         * Adds instance method's name.
376         * @param name a name of instance method of the class
377         */
378        public void addInstanceMethod(final String name)
379        {
380            instanceMethods.add(name);
381        }
382
383        /**
384         * Checks if a given name is a known instance member of the class.
385         * @param name a name to check
386         * @return true is the given name is a name of a known
387         *         instance member of the class
388         */
389        public boolean hasInstanceMember(final String name)
390        {
391            return instanceMembers.contains(name);
392        }
393
394        /**
395         * Checks if a given name is a known instance method of the class.
396         * @param name a name to check
397         * @return true is the given name is a name of a known
398         *         instance method of the class
399         */
400        public boolean hasInstanceMethod(final String name)
401        {
402            return instanceMethods.contains(name);
403        }
404
405        @Override
406        boolean contains(String nameToFind)
407        {
408            return super.contains(nameToFind)
409                    || instanceMembers.contains(nameToFind)
410                    || instanceMethods.contains(nameToFind)
411                    || staticMembers.contains(nameToFind)
412                    || staticMethods.contains(nameToFind);
413        }
414    }
415
416    /**
417     * A frame initiated on entering a statement list; holds local variable
418     * names.  For the present, I'm not worried about other class names,
419     * method names, etc.
420     * @author Stephen Bloch
421     */
422    private static class BlockFrame extends LexicalFrame
423    {
424
425        /**
426         * @param parent parent frame
427         */
428        protected BlockFrame(LexicalFrame parent)
429        {
430            super(parent);
431        }
432    }
433}