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> mFrames;
046
047    /**
048     * Frame for the currently processed AST
049     */
050    private LexicalFrame mCurrent;
051
052    @Override
053    public void beginTree(DetailAST aRootAST)
054    {
055        final Deque<LexicalFrame> aFrameStack = Lists.newLinkedList();
056        aFrameStack.add(new GlobalFrame());
057
058        mFrames = Maps.newHashMap();
059
060        DetailAST curNode = aRootAST;
061        while (curNode != null) {
062            collectDeclarations(aFrameStack, curNode);
063            DetailAST toVisit = curNode.getFirstChild();
064            while (curNode != null && toVisit == null) {
065                endCollectingDeclarations(aFrameStack, 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 aAST)
077    {
078        switch (aAST.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.mCurrent = this.mFrames.get(aAST);
087            break;
088        default :
089            // do nothing
090        }
091    } // end visitToken
092
093    /**
094     * Parse the next AST for declarations
095     *
096     * @param aFrameStack Stack containing the FrameTree being built
097     * @param aAST AST to parse
098     */
099    private void collectDeclarations(Deque<LexicalFrame> aFrameStack,
100        DetailAST aAST)
101    {
102        final LexicalFrame frame = aFrameStack.peek();
103        switch (aAST.getType()) {
104        case TokenTypes.VARIABLE_DEF :  {
105            final String name =
106                    aAST.findFirstToken(TokenTypes.IDENT).getText();
107            if (frame instanceof ClassFrame) {
108                final DetailAST mods =
109                    aAST.findFirstToken(TokenTypes.MODIFIERS);
110                if (ScopeUtils.inInterfaceBlock(aAST)
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 = aAST.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 = aAST.findFirstToken(TokenTypes.IDENT);
134            frame.addName(nameAST.getText());
135            aFrameStack.addFirst(new ClassFrame(frame));
136            break;
137        }
138        case TokenTypes.SLIST :
139            aFrameStack.addFirst(new BlockFrame(frame));
140            break;
141        case TokenTypes.METHOD_DEF : {
142            final String name = aAST.findFirstToken(TokenTypes.IDENT).getText();
143            if (frame instanceof ClassFrame) {
144                final DetailAST mods =
145                    aAST.findFirstToken(TokenTypes.MODIFIERS);
146                if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
147                    ((ClassFrame) frame).addStaticMember(name);
148                }
149                else {
150                    ((ClassFrame) frame).addInstanceMember(name);
151                }
152            }
153        }
154        case TokenTypes.CTOR_DEF :
155            aFrameStack.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 aFrameStack Stack containing the FrameTree being built
167     * @param aAST AST that was parsed
168     */
169    private void endCollectingDeclarations(Queue<LexicalFrame> aFrameStack,
170        DetailAST aAST)
171    {
172        switch (aAST.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.mFrames.put(aAST, aFrameStack.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 aName a name to check
190     * @return true is the given name is name of method or member.
191     */
192    protected final boolean isClassField(String aName)
193    {
194        final LexicalFrame frame = findFrame(aName);
195        return (frame instanceof ClassFrame)
196                && ((ClassFrame) frame).hasInstanceMember(aName);
197    }
198
199    /**
200     * Find frame containing declaration
201     * @param aName name of the declaration to find
202     * @return LexicalFrame containing declaration or null
203     */
204    private LexicalFrame findFrame(String aName)
205    {
206        if (mCurrent != null) {
207            return mCurrent.getIfContains(aName);
208        }
209        else {
210            return null;
211        }
212    }
213
214    /**
215     * A declaration frame.
216     * @author Stephen Bloch
217     * June 19, 2003
218     */
219    private abstract static class LexicalFrame
220    {
221        /** Set of name of variables declared in this frame. */
222        private final Set<String> mVarNames;
223        /**
224         * Parent frame.
225         */
226        private final LexicalFrame mParent;
227
228        /**
229         * constructor -- invokable only via super() from subclasses
230         *
231         * @param aParent parent frame
232         */
233        protected LexicalFrame(LexicalFrame aParent)
234        {
235            mParent = aParent;
236            mVarNames = Sets.newHashSet();
237        }
238
239        /** add a name to the frame.
240         * @param aNameToAdd  the name we're adding
241         */
242        void addName(String aNameToAdd)
243        {
244            mVarNames.add(aNameToAdd);
245        }
246
247        /** check whether the frame contains a given name.
248         * @param aNameToFind  the name we're looking for
249         * @return whether it was found
250         */
251        boolean contains(String aNameToFind)
252        {
253            return mVarNames.contains(aNameToFind);
254        }
255
256        /** check whether the frame contains a given name.
257         * @param aNameToFind  the name we're looking for
258         * @return whether it was found
259         */
260        LexicalFrame getIfContains(String aNameToFind)
261        {
262            if (contains(aNameToFind)) {
263                return this;
264            }
265            else if (mParent != null) {
266                return mParent.getIfContains(aNameToFind);
267            }
268            else {
269                return null;
270            }
271        }
272    }
273
274    /**
275     * The global frame; should hold only class names.
276     * @author Stephen Bloch
277     */
278    private static class GlobalFrame extends LexicalFrame
279    {
280
281        /**
282         * Constructor for the root of the FrameTree
283         */
284        protected GlobalFrame()
285        {
286            super(null);
287        }
288    }
289
290    /**
291     * A frame initiated at method definition; holds parameter names.
292     * @author Stephen Bloch
293     */
294    private static class MethodFrame extends LexicalFrame
295    {
296        /**
297         * @param aParent parent frame
298         */
299        protected MethodFrame(LexicalFrame aParent)
300        {
301            super(aParent);
302        }
303    }
304
305    /**
306     * A frame initiated at class definition; holds instance variable
307     * names.  For the present, I'm not worried about other class names,
308     * method names, etc.
309     * @author Stephen Bloch
310     */
311    private static class ClassFrame extends LexicalFrame
312    {
313        /** Set of name of instance members declared in this frame. */
314        private final Set<String> mInstanceMembers;
315        /** Set of name of variables declared in this frame. */
316        private final Set<String> mStaticMembers;
317
318        /**
319         * Creates new instance of ClassFrame
320         * @param aParent parent frame
321         */
322        public ClassFrame(LexicalFrame aParent)
323        {
324            super(aParent);
325            mInstanceMembers = Sets.newHashSet();
326            mStaticMembers = Sets.newHashSet();
327        }
328
329        /**
330         * Adds static member's name.
331         * @param aName a name of static member of the class
332         */
333        public void addStaticMember(final String aName)
334        {
335            mStaticMembers.add(aName);
336        }
337
338        /**
339         * Adds instance member's name.
340         * @param aName a name of instance member of the class
341         */
342        public void addInstanceMember(final String aName)
343        {
344            mInstanceMembers.add(aName);
345        }
346
347        /**
348         * Checks if a given name is a known instance member of the class.
349         * @param aName a name to check
350         * @return true is the given name is a name of a known
351         *         instance member of the class
352         */
353        public boolean hasInstanceMember(final String aName)
354        {
355            return mInstanceMembers.contains(aName);
356        }
357
358        @Override
359        boolean contains(String aNameToFind)
360        {
361            return super.contains(aNameToFind)
362                    || mInstanceMembers.contains(aNameToFind)
363                    || mStaticMembers.contains(aNameToFind);
364        }
365    }
366
367    /**
368     * A frame initiated on entering a statement list; holds local variable
369     * names.  For the present, I'm not worried about other class names,
370     * method names, etc.
371     * @author Stephen Bloch
372     */
373    private static class BlockFrame extends LexicalFrame
374    {
375
376        /**
377         * @param aParent parent frame
378         */
379        protected BlockFrame(LexicalFrame aParent)
380        {
381            super(aParent);
382        }
383    }
384}