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                final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
124                if (frame instanceof ClassFrame) {
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        if (current == null) {
220            return null;
221        }
222        else {
223            return current.getIfContains(name);
224        }
225    }
226
227    /**
228     * A declaration frame.
229     * @author Stephen Bloch
230     */
231    private static class LexicalFrame {
232        /** Set of name of variables declared in this frame. */
233        private final Set<String> varNames;
234        /**
235         * Parent frame.
236         */
237        private final LexicalFrame parent;
238
239        /**
240         * Constructor -- invokable only via super() from subclasses.
241         *
242         * @param parent parent frame
243         */
244        protected LexicalFrame(LexicalFrame parent) {
245            this.parent = parent;
246            varNames = new HashSet<>();
247        }
248
249        /** Add a name to the frame.
250         * @param nameToAdd the name we're adding
251         */
252        private void addName(String nameToAdd) {
253            varNames.add(nameToAdd);
254        }
255
256        /** Check whether the frame contains a given name.
257         * @param nameToFind the name we're looking for
258         * @return whether it was found
259         */
260        protected boolean contains(String nameToFind) {
261            return varNames.contains(nameToFind);
262        }
263
264        /** Check whether the frame contains a given name.
265         * @param nameToFind the name we're looking for
266         * @return whether it was found
267         */
268        private LexicalFrame getIfContains(String nameToFind) {
269            LexicalFrame frame = null;
270
271            if (contains(nameToFind)) {
272                frame = this;
273            }
274            else if (parent != null) {
275                frame = parent.getIfContains(nameToFind);
276            }
277            return frame;
278        }
279    }
280
281    /**
282     * The global frame; should hold only class names.
283     * @author Stephen Bloch
284     */
285    private static class GlobalFrame extends LexicalFrame {
286
287        /**
288         * Constructor for the root of the FrameTree.
289         */
290        protected GlobalFrame() {
291            super(null);
292        }
293    }
294
295    /**
296     * A frame initiated at method definition; holds parameter names.
297     * @author Stephen Bloch
298     */
299    private static class MethodFrame extends LexicalFrame {
300        /**
301         * Creates method frame.
302         * @param parent parent frame
303         */
304        protected MethodFrame(LexicalFrame parent) {
305            super(parent);
306        }
307    }
308
309    /**
310     * A frame initiated at class definition; holds instance variable
311     * names.  For the present, I'm not worried about other class names,
312     * method names, etc.
313     * @author Stephen Bloch
314     */
315    private static class ClassFrame extends LexicalFrame {
316        /** Set of name of instance members declared in this frame. */
317        private final Set<String> instanceMembers;
318        /** Set of name of instance methods declared in this frame. */
319        private final Set<String> instanceMethods;
320        /** Set of name of variables declared in this frame. */
321        private final Set<String> staticMembers;
322        /** Set of name of static methods declared in this frame. */
323        private final Set<String> staticMethods;
324
325        /**
326         * Creates new instance of ClassFrame.
327         * @param parent parent frame
328         */
329        ClassFrame(LexicalFrame parent) {
330            super(parent);
331            instanceMembers = new HashSet<>();
332            instanceMethods = new HashSet<>();
333            staticMembers = new HashSet<>();
334            staticMethods = new HashSet<>();
335        }
336
337        /**
338         * Adds static member's name.
339         * @param name a name of static member of the class
340         */
341        public void addStaticMember(final String name) {
342            staticMembers.add(name);
343        }
344
345        /**
346         * Adds static method's name.
347         * @param name a name of static method of the class
348         */
349        public void addStaticMethod(final String name) {
350            staticMethods.add(name);
351        }
352
353        /**
354         * Adds instance member's name.
355         * @param name a name of instance member of the class
356         */
357        public void addInstanceMember(final String name) {
358            instanceMembers.add(name);
359        }
360
361        /**
362         * Adds instance method's name.
363         * @param name a name of instance method of the class
364         */
365        public void addInstanceMethod(final String name) {
366            instanceMethods.add(name);
367        }
368
369        /**
370         * Checks if a given name is a known instance member of the class.
371         * @param name a name to check
372         * @return true is the given name is a name of a known
373         *         instance member of the class
374         */
375        public boolean hasInstanceMember(final String name) {
376            return instanceMembers.contains(name);
377        }
378
379        /**
380         * Checks if a given name is a known instance method of the class.
381         * @param name a name to check
382         * @return true is the given name is a name of a known
383         *         instance method of the class
384         */
385        public boolean hasInstanceMethod(final String name) {
386            return instanceMethods.contains(name);
387        }
388
389        @Override
390        protected boolean contains(String nameToFind) {
391            return super.contains(nameToFind)
392                    || instanceMembers.contains(nameToFind)
393                    || instanceMethods.contains(nameToFind)
394                    || staticMembers.contains(nameToFind)
395                    || staticMethods.contains(nameToFind);
396        }
397    }
398
399    /**
400     * A frame initiated on entering a statement list; holds local variable
401     * names.  For the present, I'm not worried about other class names,
402     * method names, etc.
403     * @author Stephen Bloch
404     */
405    private static class BlockFrame extends LexicalFrame {
406
407        /**
408         * Creates block frame.
409         * @param parent parent frame
410         */
411        protected BlockFrame(LexicalFrame parent) {
412            super(parent);
413        }
414    }
415}