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.blocks; 021 022 023import com.puppycrawl.tools.checkstyle.api.Check; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026 027/** 028 * <p> 029 * Checks for braces around code blocks. 030 * </p> 031 * <p> By default the check will check the following blocks: 032 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 033 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}, 034 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 035 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 036 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}. 037 * </p> 038 * <p> 039 * An example of how to configure the check is: 040 * </p> 041 * <pre> 042 * <module name="NeedBraces"/> 043 * </pre> 044 * <p> An example of how to configure the check for <code>if</code> and 045 * <code>else</code> blocks is: 046 * </p> 047 * <pre> 048 * <module name="NeedBraces"> 049 * <property name="tokens" value="LITERAL_IF, LITERAL_ELSE"/> 050 * </module> 051 * </pre> 052 * Check has an option <b>allowSingleLineIf</b> which allows one line 053 * if-statements without braces, e.g.: 054 * <p> 055 * <code> 056 * if (obj.isValid()) return true; 057 * </code> 058 * </p> 059 * <br> 060 * 061 * @author Rick Giles 062 * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a> 063 * @version 1.0 064 */ 065public class NeedBracesCheck extends Check 066{ 067 /** 068 * Check's option for skipping single-line if-statements. 069 */ 070 private boolean allowSingleLineIf; 071 072 /** 073 * A key is pointing to the warning message text in "messages.properties" 074 * file. 075 */ 076 public static final String MSG_KEY_NEED_BRACES = "needBraces"; 077 078 /** 079 * Setter. 080 * @param allowSingleLineIf Check's option for skipping single-line if-statements 081 */ 082 public void setAllowSingleLineIf(boolean allowSingleLineIf) 083 { 084 this.allowSingleLineIf = allowSingleLineIf; 085 } 086 087 @Override 088 public int[] getDefaultTokens() 089 { 090 return new int[] { 091 TokenTypes.LITERAL_DO, 092 TokenTypes.LITERAL_ELSE, 093 TokenTypes.LITERAL_FOR, 094 TokenTypes.LITERAL_IF, 095 TokenTypes.LITERAL_WHILE, 096 }; 097 } 098 099 @Override 100 public void visitToken(DetailAST ast) 101 { 102 final DetailAST slistAST = ast.findFirstToken(TokenTypes.SLIST); 103 boolean isElseIf = false; 104 if ((ast.getType() == TokenTypes.LITERAL_ELSE) 105 && (ast.findFirstToken(TokenTypes.LITERAL_IF) != null)) 106 { 107 isElseIf = true; 108 } 109 boolean skipStatement = false; 110 if (ast.getType() == TokenTypes.LITERAL_IF) { 111 skipStatement = isSkipIfBlock(ast); 112 } 113 if ((slistAST == null) && !isElseIf && !skipStatement) { 114 log(ast.getLineNo(), MSG_KEY_NEED_BRACES, ast.getText()); 115 } 116 } 117 118 /** 119 * Checks if current if-block can be skipped by "need braces" warning. 120 * @param literalIf {@link TokenTypes#LITERAL_IF LITERAL_IF} 121 * @return true if current if block can be skipped by Check 122 */ 123 private boolean isSkipIfBlock(DetailAST literalIf) 124 { 125 return allowSingleLineIf && isSingleLineIf(literalIf); 126 } 127 128 /** 129 * Checks if current if-statement is single-line statement, e.g.: 130 * <p> 131 * <code> 132 * if (obj.isValid()) return true; 133 * </code> 134 * </p> 135 * @param literalIf {@link TokenTypes#LITERAL_IF LITERAL_IF} 136 * @return true if current if-statement is single-line statement 137 */ 138 private static boolean isSingleLineIf(DetailAST literalIf) 139 { 140 boolean result = false; 141 final DetailAST ifBlock = literalIf.getLastChild(); 142 final DetailAST lastElementInIfBlock = ifBlock.getLastChild(); 143 if (lastElementInIfBlock != null 144 && lastElementInIfBlock.getFirstChild() == null 145 && literalIf.getLineNo() == lastElementInIfBlock.getLineNo()) 146 { 147 result = true; 148 } 149 return result; 150 } 151}