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.coding;
021
022import java.util.Deque;
023
024import antlr.collections.AST;
025
026import com.google.common.collect.Lists;
027import com.puppycrawl.tools.checkstyle.api.Check;
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 Check {
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    @Override
052    public int[] getAcceptableTokens() {
053        return new int[] {
054            TokenTypes.METHOD_DEF,
055            TokenTypes.LITERAL_SUPER,
056        };
057    }
058
059    @Override
060    public int[] getDefaultTokens() {
061        return getAcceptableTokens();
062    }
063
064    @Override
065    public int[] getRequiredTokens() {
066        return getDefaultTokens();
067    }
068
069    /**
070     * Returns the name of the overriding method.
071     * @return the name of the overriding method.
072     */
073    protected abstract String getMethodName();
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 (dotAst.getType() == TokenTypes.DOT
105                && !isSameNameMethod(literalSuperAst)
106                && !hasArguments(dotAst)) {
107                superCall = isSuperCallInOverridingMethod(dotAst);
108            }
109        }
110        return superCall;
111    }
112
113    /**
114     * Determines whether a super call in overriding method.
115     *
116     * @param ast The AST node of a 'dot operator' in 'super' call.
117     * @return true if super call in overriding method.
118     */
119    private boolean isSuperCallInOverridingMethod(DetailAST ast) {
120        boolean inOverridingMethod = false;
121        DetailAST dotAst = ast;
122
123        while (dotAst != null
124                && dotAst.getType() != TokenTypes.CTOR_DEF
125                && dotAst.getType() != TokenTypes.INSTANCE_INIT) {
126
127            if (dotAst.getType() == TokenTypes.METHOD_DEF) {
128                inOverridingMethod = isOverridingMethod(dotAst);
129                break;
130            }
131            dotAst = dotAst.getParent();
132
133        }
134        return inOverridingMethod;
135    }
136
137    /**
138     * Does method have any arguments.
139     * @param methodCallDotAst DOT DetailAST
140     * @return true if any parameters found
141     */
142    private static boolean hasArguments(DetailAST methodCallDotAst) {
143        final DetailAST argumentsList = methodCallDotAst.getNextSibling();
144        return argumentsList.getChildCount() > 0;
145    }
146
147    /**
148     * Is same name of method.
149     * @param ast method AST
150     * @return true if method name is the same
151     */
152    private boolean isSameNameMethod(DetailAST ast) {
153
154        AST sibling = ast.getNextSibling();
155        // ignore type parameters
156        if (sibling != null
157            && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
158            sibling = sibling.getNextSibling();
159        }
160        if (sibling == null || sibling.getType() != TokenTypes.IDENT) {
161            return true;
162        }
163        final String name = sibling.getText();
164        return !getMethodName().equals(name);
165    }
166
167    @Override
168    public void leaveToken(DetailAST ast) {
169        if (isOverridingMethod(ast)) {
170            final MethodNode methodNode =
171                methodStack.removeLast();
172            if (!methodNode.isCallingSuper()) {
173                final DetailAST methodAST = methodNode.getMethod();
174                final DetailAST nameAST =
175                    methodAST.findFirstToken(TokenTypes.IDENT);
176                log(nameAST.getLineNo(), nameAST.getColumnNo(),
177                    MSG_KEY, nameAST.getText());
178            }
179        }
180    }
181
182    /**
183     * Determines whether an AST is a method definition for this check,
184     * with 0 parameters.
185     * @param ast the method definition AST.
186     * @return true if the method of ast is a method for this check.
187     */
188    private boolean isOverridingMethod(DetailAST ast) {
189        boolean overridingMethod = false;
190
191        if (ast.getType() == TokenTypes.METHOD_DEF
192                && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
193            final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
194            final String name = nameAST.getText();
195            final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
196
197            if (getMethodName().equals(name)
198                    && !modifiersAST.branchContains(TokenTypes.LITERAL_NATIVE)) {
199                final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
200                overridingMethod = params.getChildCount() == 0;
201            }
202        }
203        return overridingMethod;
204    }
205
206    /**
207     * Stack node for a method definition and a record of
208     * whether the method has a call to the super method.
209     * @author Rick Giles
210     */
211    private static class MethodNode {
212        /** Method definition. */
213        private final DetailAST method;
214
215        /** True if the overriding method calls the super method. */
216        private boolean callingSuper;
217
218        /**
219         * Constructs a stack node for a method definition.
220         * @param ast AST for the method definition.
221         */
222        MethodNode(DetailAST ast) {
223            method = ast;
224            callingSuper = false;
225        }
226
227        /**
228         * Records that the overriding method has a call to the super method.
229         */
230        public void setCallingSuper() {
231            callingSuper = true;
232        }
233
234        /**
235         * Determines whether the overriding method has a call to the super
236         * method.
237         * @return true if the overriding method has a call to the super method.
238         */
239        public boolean isCallingSuper() {
240            return callingSuper;
241        }
242
243        /**
244         * Returns the overriding method definition AST.
245         * @return the overriding method definition AST.
246         */
247        public DetailAST getMethod() {
248            return method;
249        }
250    }
251}