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