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 mMethod;
049
050        /** true if the overriding method calls the super method */
051        private boolean mCallsSuper;
052
053        /**
054         * Constructs a stack node for a method definition.
055         * @param aAST AST for the method definition.
056         */
057        public MethodNode(DetailAST aAST)
058        {
059            mMethod = aAST;
060            mCallsSuper = false;
061        }
062
063        /**
064         * Records that the overriding method has a call to the super method.
065         */
066        public void setCallsSuper()
067        {
068            mCallsSuper = 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 mCallsSuper;
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 mMethod;
089        }
090    }
091
092    /** stack of methods */
093    private final LinkedList<MethodNode> mMethodStack = 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 aRootAST)
112    {
113        mMethodStack.clear();
114    }
115
116    @Override
117    public void visitToken(DetailAST aAST)
118    {
119        if (isOverridingMethod(aAST)) {
120            mMethodStack.add(new MethodNode(aAST));
121        }
122        else if (isSuperCall(aAST)) {
123            final MethodNode methodNode = mMethodStack.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 aAST the AST node of a 'super' literal.
132     * @return true if aAST is a call to the super method
133     * for this check.
134     */
135    private boolean isSuperCall(DetailAST aAST)
136    {
137        if (aAST.getType() != TokenTypes.LITERAL_SUPER) {
138            return false;
139        }
140        // dot operator?
141        DetailAST parent = aAST.getParent();
142        if ((parent == null) || (parent.getType() != TokenTypes.DOT)) {
143            return false;
144        }
145
146        // same name of method
147        AST sibling = aAST.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 aAST)
188    {
189        if (isOverridingMethod(aAST)) {
190            final MethodNode methodNode =
191                mMethodStack.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 aAST the method definition AST.
206     * @return true if the method of aAST is a method for this check.
207     */
208    private boolean isOverridingMethod(DetailAST aAST)
209    {
210        if ((aAST.getType() != TokenTypes.METHOD_DEF)
211            || ScopeUtils.inInterfaceOrAnnotationBlock(aAST))
212        {
213            return false;
214        }
215        final DetailAST nameAST = aAST.findFirstToken(TokenTypes.IDENT);
216        final String name = nameAST.getText();
217        if (!getMethodName().equals(name)) {
218            return false;
219        }
220        final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS);
221        return (params.getChildCount() == 0);
222    }
223}