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.puppycrawl.tools.checkstyle.api.Check;
024import com.puppycrawl.tools.checkstyle.api.TokenTypes;
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026
027
028/**
029 * <p>
030 * Checks for overly complicated boolean return statements.
031 * Idea shamelessly stolen from the equivalent PMD rule (pmd.sourceforge.net).
032 * </p>
033 * <p>
034 * An example of how to configure the check is:
035 * </p>
036 * <pre>
037 * &lt;module name="SimplifyBooleanReturn"/&gt;
038 * </pre>
039 * @author Lars Kühne
040 */
041public class SimplifyBooleanReturnCheck
042    extends Check
043{
044    @Override
045    public int[] getDefaultTokens()
046    {
047        return new int[] {TokenTypes.LITERAL_IF};
048    }
049
050    @Override
051    public void visitToken(DetailAST ast)
052    {
053        // LITERAL_IF has the following four or five children:
054        // '('
055        // condition
056        // ')'
057        // thenStatement
058        // [ LITERAL_ELSE (with the elseStatement as a child) ]
059
060        // don't bother if this is not if then else
061        final AST elseLiteral =
062            ast.findFirstToken(TokenTypes.LITERAL_ELSE);
063        if (elseLiteral == null) {
064            return;
065        }
066        final AST elseStatement = elseLiteral.getFirstChild();
067
068        // skip '(' and ')'
069        // TODO: Introduce helpers in DetailAST
070        final AST condition = ast.getFirstChild().getNextSibling();
071        final AST thenStatement = condition.getNextSibling().getNextSibling();
072
073        if (returnsOnlyBooleanLiteral(thenStatement)
074            && returnsOnlyBooleanLiteral(elseStatement))
075        {
076            log(ast.getLineNo(), ast.getColumnNo(), "simplify.boolreturn");
077        }
078    }
079
080    /**
081     * Returns if an AST is a return statment with a boolean literal
082     * or a compound statement that contains only such a return statement.
083     *
084     * Returns <code>true</code> iff ast represents
085     * <br/>
086     * <pre>
087     * return true/false;
088     * </pre>
089     * or
090     * <br/>
091     * <pre>
092     * {
093     *   return true/false;
094     * }
095     * </pre>
096     *
097     * @param ast the sytax tree to check
098     * @return if ast is a return statment with a boolean literal.
099     */
100    private static boolean returnsOnlyBooleanLiteral(AST ast)
101    {
102        if (isBooleanLiteralReturnStatement(ast)) {
103            return true;
104        }
105
106        final AST firstStmnt = ast.getFirstChild();
107        return isBooleanLiteralReturnStatement(firstStmnt);
108    }
109
110    /**
111     * Returns if an AST is a return statment with a boolean literal.
112     *
113     * Returns <code>true</code> iff ast represents
114     * <br/>
115     * <pre>
116     * return true/false;
117     * </pre>
118     *
119     * @param ast the sytax tree to check
120     * @return if ast is a return statment with a boolean literal.
121     */
122    private static boolean isBooleanLiteralReturnStatement(AST ast)
123    {
124        if ((ast == null) || (ast.getType() != TokenTypes.LITERAL_RETURN)) {
125            return false;
126        }
127
128        final AST expr = ast.getFirstChild();
129
130        if ((expr == null) || (expr.getType() == TokenTypes.SEMI)) {
131            return false;
132        }
133
134        final AST value = expr.getFirstChild();
135        return isBooleanLiteralType(value.getType());
136    }
137
138    /**
139     * Checks if a token type is a literal true or false.
140     * @param tokenType the TokenType
141     * @return true iff tokenType is LITERAL_TRUE or LITERAL_FALSE
142     */
143    private static boolean isBooleanLiteralType(final int tokenType)
144    {
145        final boolean iastrue = (tokenType == TokenTypes.LITERAL_TRUE);
146        final boolean isFalse = (tokenType == TokenTypes.LITERAL_FALSE);
147        return iastrue || isFalse;
148    }
149}