001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.HashMap;
024import java.util.HashSet;
025import java.util.LinkedList;
026import java.util.Map;
027import java.util.Queue;
028import java.util.Set;
029
030import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
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
039 *             {@link AbstractCheck} instead.
040 * @author o_sukhodolsky
041 * @noinspection AbstractClassNeverImplemented
042 */
043@Deprecated
044public abstract class AbstractDeclarationCollector extends AbstractCheck {
045    /**
046     * Tree of all the parsed frames.
047     */
048    private Map<DetailAST, LexicalFrame> frames;
049
050    /**
051     * Frame for the currently processed AST.
052     */
053    private LexicalFrame current;
054
055    @Override
056    public void beginTree(DetailAST rootAST) {
057        final Deque<LexicalFrame> frameStack = new LinkedList<>();
058        frameStack.add(new GlobalFrame());
059
060        frames = new HashMap<>();
061
062        DetailAST curNode = rootAST;
063        while (curNode != null) {
064            collectDeclarations(frameStack, curNode);
065            DetailAST toVisit = curNode.getFirstChild();
066            while (curNode != null && toVisit == null) {
067                endCollectingDeclarations(frameStack, curNode);
068                toVisit = curNode.getNextSibling();
069                if (toVisit == null) {
070                    curNode = curNode.getParent();
071                }
072            }
073            curNode = toVisit;
074        }
075    }
076
077    @Override
078    public void visitToken(DetailAST ast) {
079        switch (ast.getType()) {
080            case TokenTypes.CLASS_DEF :
081            case TokenTypes.INTERFACE_DEF :
082            case TokenTypes.ENUM_DEF :
083            case TokenTypes.ANNOTATION_DEF :
084            case TokenTypes.SLIST :
085            case TokenTypes.METHOD_DEF :
086            case TokenTypes.CTOR_DEF :
087                current = frames.get(ast);
088                break;
089            default :
090                // do nothing
091        }
092    }
093
094    /**
095     * Parse the next AST for declarations.
096     *
097     * @param frameStack Stack containing the FrameTree being built
098     * @param ast AST to parse
099     */
100    private static void collectDeclarations(Deque<LexicalFrame> frameStack,
101        DetailAST ast) {
102        final LexicalFrame frame = frameStack.peek();
103        switch (ast.getType()) {
104            case TokenTypes.VARIABLE_DEF :
105                collectVariableDeclarations(ast, frame);
106                break;
107            case TokenTypes.PARAMETER_DEF :
108                final DetailAST parameterAST = ast.findFirstToken(TokenTypes.IDENT);
109                frame.addName(parameterAST.getText());
110                break;
111            case TokenTypes.CLASS_DEF :
112            case TokenTypes.INTERFACE_DEF :
113            case TokenTypes.ENUM_DEF :
114            case TokenTypes.ANNOTATION_DEF :
115                final DetailAST classAST = ast.findFirstToken(TokenTypes.IDENT);
116                frame.addName(classAST.getText());
117                frameStack.addFirst(new ClassFrame(frame));
118                break;
119            case TokenTypes.SLIST :
120                frameStack.addFirst(new BlockFrame(frame));
121                break;
122            case TokenTypes.METHOD_DEF :
123                if (frame instanceof ClassFrame) {
124                    final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
125                    final DetailAST mods =
126                            ast.findFirstToken(TokenTypes.MODIFIERS);
127                    if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
128                        ((ClassFrame) frame).addStaticMethod(name);
129                    }
130                    else {
131                        ((ClassFrame) frame).addInstanceMethod(name);
132                    }
133                }
134                frameStack.addFirst(new MethodFrame(frame));
135                break;
136            case TokenTypes.CTOR_DEF :
137                frameStack.addFirst(new MethodFrame(frame));
138                break;
139            default:
140                // do nothing
141        }
142    }
143
144    /**
145     * Collect Variable Declarations.
146     * @param ast variable token
147     * @param frame current frame
148     */
149    private static void collectVariableDeclarations(DetailAST ast, LexicalFrame frame) {
150        final String name =
151                ast.findFirstToken(TokenTypes.IDENT).getText();
152        if (frame instanceof ClassFrame) {
153            final DetailAST mods =
154                    ast.findFirstToken(TokenTypes.MODIFIERS);
155            if (ScopeUtils.isInInterfaceBlock(ast)
156                    || mods.branchContains(TokenTypes.LITERAL_STATIC)) {
157                ((ClassFrame) frame).addStaticMember(name);
158            }
159            else {
160                ((ClassFrame) frame).addInstanceMember(name);
161            }
162        }
163        else {
164            frame.addName(name);
165        }
166    }
167
168    /**
169     * End parsing of the AST for declarations.
170     *
171     * @param frameStack Stack containing the FrameTree being built
172     * @param ast AST that was parsed
173     */
174    private void endCollectingDeclarations(Queue<LexicalFrame> frameStack,
175        DetailAST ast) {
176        switch (ast.getType()) {
177            case TokenTypes.CLASS_DEF :
178            case TokenTypes.INTERFACE_DEF :
179            case TokenTypes.ENUM_DEF :
180            case TokenTypes.ANNOTATION_DEF :
181            case TokenTypes.SLIST :
182            case TokenTypes.METHOD_DEF :
183            case TokenTypes.CTOR_DEF :
184                frames.put(ast, frameStack.poll());
185                break;
186            default :
187                // do nothing
188        }
189    }
190
191    /**
192     * Check if given name is a name for class field in current environment.
193     * @param name a name to check
194     * @return true is the given name is name of member.
195     */
196    protected final boolean isClassField(String name) {
197        final LexicalFrame frame = findFrame(name);
198        return frame instanceof ClassFrame
199                && ((ClassFrame) frame).hasInstanceMember(name);
200    }
201
202    /**
203     * Check if given name is a name for class method in current environment.
204     * @param name a name to check
205     * @return true is the given name is name of method.
206     */
207    protected final boolean isClassMethod(String name) {
208        final LexicalFrame frame = findFrame(name);
209        return frame instanceof ClassFrame
210                && ((ClassFrame) frame).hasInstanceMethod(name);
211    }
212
213    /**
214     * Find frame containing declaration.
215     * @param name name of the declaration to find
216     * @return LexicalFrame containing declaration or null
217     */
218    private LexicalFrame findFrame(String name) {
219        LexicalFrame frame = null;
220        if (current != null) {
221            frame = current.getIfContains(name);
222        }
223        return frame;
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 = new HashSet<>();
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        protected 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 = new HashSet<>();
331            instanceMethods = new HashSet<>();
332            staticMembers = new HashSet<>();
333            staticMethods = new HashSet<>();
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        protected 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}