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.blocks; 021 022import org.apache.commons.lang3.ArrayUtils; 023import org.apache.commons.lang3.StringUtils; 024 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck; 028 029/** 030 * Checks for empty blocks. The policy to verify is specified using the {@link 031 * BlockOption} class and defaults to {@link BlockOption#STMT}. 032 * 033 * <p> By default the check will check the following blocks: 034 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, 035 * {@link TokenTypes#LITERAL_TRY LITERAL_TRY}, 036 * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY}, 037 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 038 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 039 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}, 040 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 041 * {@link TokenTypes#STATIC_INIT STATIC_INIT}, 042 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}. 043 * {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}. 044 * </p> 045 * 046 * <p> An example of how to configure the check is: 047 * </p> 048 * <pre> 049 * <module name="EmptyBlock"/> 050 * </pre> 051 * 052 * <p> An example of how to configure the check for the {@link 053 * BlockOption#TEXT} policy and only try blocks is: 054 * </p> 055 * 056 * <pre> 057 * <module name="EmptyBlock"> 058 * <property name="tokens" value="LITERAL_TRY"/> 059 * <property name="option" value="text"/> 060 * </module> 061 * </pre> 062 * 063 * @author Lars Kühne 064 */ 065public class EmptyBlockCheck 066 extends AbstractOptionCheck<BlockOption> { 067 /** 068 * A key is pointing to the warning message text in "messages.properties" 069 * file. 070 */ 071 public static final String MSG_KEY_BLOCK_NO_STMT = "block.noStmt"; 072 073 /** 074 * A key is pointing to the warning message text in "messages.properties" 075 * file. 076 */ 077 public static final String MSG_KEY_BLOCK_EMPTY = "block.empty"; 078 079 /** 080 * Creates a new {@code EmptyBlockCheck} instance. 081 */ 082 public EmptyBlockCheck() { 083 super(BlockOption.STMT, BlockOption.class); 084 } 085 086 @Override 087 public int[] getDefaultTokens() { 088 return new int[] { 089 TokenTypes.LITERAL_WHILE, 090 TokenTypes.LITERAL_TRY, 091 TokenTypes.LITERAL_FINALLY, 092 TokenTypes.LITERAL_DO, 093 TokenTypes.LITERAL_IF, 094 TokenTypes.LITERAL_ELSE, 095 TokenTypes.LITERAL_FOR, 096 TokenTypes.INSTANCE_INIT, 097 TokenTypes.STATIC_INIT, 098 TokenTypes.LITERAL_SWITCH, 099 TokenTypes.LITERAL_SYNCHRONIZED, 100 }; 101 } 102 103 @Override 104 public int[] getAcceptableTokens() { 105 return new int[] { 106 TokenTypes.LITERAL_WHILE, 107 TokenTypes.LITERAL_TRY, 108 TokenTypes.LITERAL_CATCH, 109 TokenTypes.LITERAL_FINALLY, 110 TokenTypes.LITERAL_DO, 111 TokenTypes.LITERAL_IF, 112 TokenTypes.LITERAL_ELSE, 113 TokenTypes.LITERAL_FOR, 114 TokenTypes.INSTANCE_INIT, 115 TokenTypes.STATIC_INIT, 116 TokenTypes.LITERAL_SWITCH, 117 TokenTypes.LITERAL_SYNCHRONIZED, 118 TokenTypes.LITERAL_CASE, 119 TokenTypes.LITERAL_DEFAULT, 120 TokenTypes.ARRAY_INIT, 121 }; 122 } 123 124 @Override 125 public int[] getRequiredTokens() { 126 return ArrayUtils.EMPTY_INT_ARRAY; 127 } 128 129 @Override 130 public void visitToken(DetailAST ast) { 131 final DetailAST slistToken = ast.findFirstToken(TokenTypes.SLIST); 132 final DetailAST leftCurly; 133 134 if (slistToken == null) { 135 leftCurly = ast.findFirstToken(TokenTypes.LCURLY); 136 } 137 else { 138 leftCurly = slistToken; 139 } 140 141 if (leftCurly != null) { 142 if (getAbstractOption() == BlockOption.STMT) { 143 boolean emptyBlock; 144 if (leftCurly.getType() == TokenTypes.LCURLY) { 145 emptyBlock = leftCurly.getNextSibling().getType() != TokenTypes.CASE_GROUP; 146 } 147 else { 148 emptyBlock = leftCurly.getChildCount() <= 1; 149 } 150 if (emptyBlock) { 151 log(leftCurly.getLineNo(), 152 leftCurly.getColumnNo(), 153 MSG_KEY_BLOCK_NO_STMT, 154 ast.getText()); 155 } 156 } 157 else if (!hasText(leftCurly)) { 158 log(leftCurly.getLineNo(), 159 leftCurly.getColumnNo(), 160 MSG_KEY_BLOCK_EMPTY, 161 ast.getText()); 162 } 163 } 164 } 165 166 /** 167 * @param slistAST a {@code DetailAST} value 168 * @return whether the SLIST token contains any text. 169 */ 170 protected boolean hasText(final DetailAST slistAST) { 171 final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY); 172 final DetailAST rcurlyAST; 173 174 if (rightCurly == null) { 175 rcurlyAST = slistAST.getParent().findFirstToken(TokenTypes.RCURLY); 176 } 177 else { 178 rcurlyAST = rightCurly; 179 } 180 final int slistLineNo = slistAST.getLineNo(); 181 final int slistColNo = slistAST.getColumnNo(); 182 final int rcurlyLineNo = rcurlyAST.getLineNo(); 183 final int rcurlyColNo = rcurlyAST.getColumnNo(); 184 final String[] lines = getLines(); 185 boolean retVal = false; 186 if (slistLineNo == rcurlyLineNo) { 187 // Handle braces on the same line 188 final String txt = lines[slistLineNo - 1] 189 .substring(slistColNo + 1, rcurlyColNo); 190 if (StringUtils.isNotBlank(txt)) { 191 retVal = true; 192 } 193 } 194 else { 195 // check only whitespace of first & last lines 196 if (!lines[slistLineNo - 1] 197 .substring(slistColNo + 1).trim().isEmpty() 198 || !lines[rcurlyLineNo - 1] 199 .substring(0, rcurlyColNo).trim().isEmpty()) { 200 retVal = true; 201 } 202 else { 203 // check if all lines are also only whitespace 204 retVal = !checkIsAllLinesAreWhitespace(lines, slistLineNo, rcurlyLineNo); 205 } 206 } 207 return retVal; 208 } 209 210 /** 211 * Checks is all lines in array contain whitespaces only. 212 * 213 * @param lines 214 * array of lines 215 * @param lineFrom 216 * check from this line number 217 * @param lineTo 218 * check to this line numbers 219 * @return true if lines contain only whitespaces 220 */ 221 private static boolean checkIsAllLinesAreWhitespace(String[] lines, int lineFrom, int lineTo) { 222 boolean result = true; 223 for (int i = lineFrom; i < lineTo - 1; i++) { 224 if (!lines[i].trim().isEmpty()) { 225 result = false; 226 break; 227 } 228 } 229 return result; 230 } 231}