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