001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 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.coding;
021
022import java.util.Deque;
023
024import antlr.collections.AST;
025
026import com.google.common.collect.Lists;
027import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.TokenTypes;
030import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
031
032/**
033 * <p>
034 * Abstract class for checking that an overriding method with no parameters
035 * invokes the super method.
036 * </p>
037 * @author Rick Giles
038 */
039public abstract class AbstractSuperCheck
040        extends AbstractCheck {
041
042    /**
043     * A key is pointing to the warning message text in "messages.properties"
044     * file.
045     */
046    public static final String MSG_KEY = "missing.super.call";
047
048    /** Stack of methods. */
049    private final Deque<MethodNode> methodStack = Lists.newLinkedList();
050
051    /**
052     * Returns the name of the overriding method.
053     * @return the name of the overriding method.
054     */
055    protected abstract String getMethodName();
056
057    @Override
058    public int[] getAcceptableTokens() {
059        return new int[] {
060            TokenTypes.METHOD_DEF,
061            TokenTypes.LITERAL_SUPER,
062        };
063    }
064
065    @Override
066    public int[] getDefaultTokens() {
067        return getAcceptableTokens();
068    }
069
070    @Override
071    public int[] getRequiredTokens() {
072        return getDefaultTokens();
073    }
074
075    @Override
076    public void beginTree(DetailAST rootAST) {
077        methodStack.clear();
078    }
079
080    @Override
081    public void visitToken(DetailAST ast) {
082        if (isOverridingMethod(ast)) {
083            methodStack.add(new MethodNode(ast));
084        }
085        else if (isSuperCall(ast)) {
086            final MethodNode methodNode = methodStack.getLast();
087            methodNode.setCallingSuper();
088        }
089    }
090
091    /**
092     * Determines whether a 'super' literal is a call to the super method
093     * for this check.
094     * @param literalSuperAst the AST node of a 'super' literal.
095     * @return true if ast is a call to the super method for this check.
096     */
097    private boolean isSuperCall(DetailAST literalSuperAst) {
098        boolean superCall = false;
099
100        if (literalSuperAst.getType() == TokenTypes.LITERAL_SUPER) {
101            // dot operator?
102            final DetailAST dotAst = literalSuperAst.getParent();
103
104            if (!isSameNameMethod(literalSuperAst)
105                && !hasArguments(dotAst)) {
106                superCall = isSuperCallInOverridingMethod(dotAst);
107            }
108        }
109        return superCall;
110    }
111
112    /**
113     * Determines whether a super call in overriding method.
114     *
115     * @param ast The AST node of a 'dot operator' in 'super' call.
116     * @return true if super call in overriding method.
117     */
118    private boolean isSuperCallInOverridingMethod(DetailAST ast) {
119        boolean inOverridingMethod = false;
120        DetailAST dotAst = ast;
121
122        while (dotAst.getType() != TokenTypes.CTOR_DEF
123                && dotAst.getType() != TokenTypes.INSTANCE_INIT) {
124
125            if (dotAst.getType() == TokenTypes.METHOD_DEF) {
126                inOverridingMethod = isOverridingMethod(dotAst);
127                break;
128            }
129            dotAst = dotAst.getParent();
130
131        }
132        return inOverridingMethod;
133    }
134
135    /**
136     * Does method have any arguments.
137     * @param methodCallDotAst DOT DetailAST
138     * @return true if any parameters found
139     */
140    private static boolean hasArguments(DetailAST methodCallDotAst) {
141        final DetailAST argumentsList = methodCallDotAst.getNextSibling();
142        return argumentsList.getChildCount() > 0;
143    }
144
145    /**
146     * Is same name of method.
147     * @param ast method AST
148     * @return true if method name is the same
149     */
150    private boolean isSameNameMethod(DetailAST ast) {
151
152        AST sibling = ast.getNextSibling();
153        // ignore type parameters
154        if (sibling != null
155            && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
156            sibling = sibling.getNextSibling();
157        }
158        if (sibling == null) {
159            return true;
160        }
161        final String name = sibling.getText();
162        return !getMethodName().equals(name);
163    }
164
165    @Override
166    public void leaveToken(DetailAST ast) {
167        if (isOverridingMethod(ast)) {
168            final MethodNode methodNode =
169                methodStack.removeLast();
170            if (!methodNode.isCallingSuper()) {
171                final DetailAST methodAST = methodNode.getMethod();
172                final DetailAST nameAST =
173                    methodAST.findFirstToken(TokenTypes.IDENT);
174                log(nameAST.getLineNo(), nameAST.getColumnNo(),
175                    MSG_KEY, nameAST.getText());
176            }
177        }
178    }
179
180    /**
181     * Determines whether an AST is a method definition for this check,
182     * with 0 parameters.
183     * @param ast the method definition AST.
184     * @return true if the method of ast is a method for this check.
185     */
186    private boolean isOverridingMethod(DetailAST ast) {
187        boolean overridingMethod = false;
188
189        if (ast.getType() == TokenTypes.METHOD_DEF
190                && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
191            final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
192            final String name = nameAST.getText();
193            final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
194
195            if (getMethodName().equals(name)
196                    && !modifiersAST.branchContains(TokenTypes.LITERAL_NATIVE)) {
197                final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
198                overridingMethod = params.getChildCount() == 0;
199            }
200        }
201        return overridingMethod;
202    }
203
204    /**
205     * Stack node for a method definition and a record of
206     * whether the method has a call to the super method.
207     * @author Rick Giles
208     */
209    private static class MethodNode {
210        /** Method definition. */
211        private final DetailAST method;
212
213        /** True if the overriding method calls the super method. */
214        private boolean callingSuper;
215
216        /**
217         * Constructs a stack node for a method definition.
218         * @param ast AST for the method definition.
219         */
220        MethodNode(DetailAST ast) {
221            method = ast;
222            callingSuper = false;
223        }
224
225        /**
226         * Records that the overriding method has a call to the super method.
227         */
228        public void setCallingSuper() {
229            callingSuper = true;
230        }
231
232        /**
233         * Determines whether the overriding method has a call to the super
234         * method.
235         * @return true if the overriding method has a call to the super method.
236         */
237        public boolean isCallingSuper() {
238            return callingSuper;
239        }
240
241        /**
242         * Returns the overriding method definition AST.
243         * @return the overriding method definition AST.
244         */
245        public DetailAST getMethod() {
246            return method;
247        }
248    }
249}