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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.blocks; 020 021import com.puppycrawl.tools.checkstyle.api.DetailAST; 022import com.puppycrawl.tools.checkstyle.api.TokenTypes; 023import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck; 024 025/** 026 * Checks for empty blocks. The policy to verify is specified using the {@link 027 * BlockOption} class and defaults to {@link BlockOption#STMT}. 028 * 029 * <p> By default the check will check the following blocks: 030 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, 031 * {@link TokenTypes#LITERAL_TRY LITERAL_TRY}, 032 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, 033 * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY}, 034 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 035 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 036 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}, 037 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 038 * {@link TokenTypes#STATIC_INIT STATIC_INIT}, 039 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}. 040 * </p> 041 * 042 * <p> An example of how to configure the check is: 043 * </p> 044 * <pre> 045 * <module name="EmptyBlock"/> 046 * </pre> 047 * 048 * <p> An example of how to configure the check for the {@link 049 * BlockOption#TEXT} policy and only catch blocks is: 050 * </p> 051 * 052 * <pre> 053 * <module name="EmptyBlock"> 054 * <property name="tokens" value="LITERAL_CATCH"/> 055 * <property name="option" value="text"/> 056 * </module> 057 * </pre> 058 * 059 * @author Lars K?hne 060 */ 061public class EmptyBlockCheck 062 extends AbstractOptionCheck<BlockOption> 063{ 064 /** 065 * Creates a new <code>EmptyBlockCheck</code> instance. 066 */ 067 public EmptyBlockCheck() 068 { 069 super(BlockOption.STMT, BlockOption.class); 070 } 071 072 @Override 073 public int[] getDefaultTokens() 074 { 075 return new int[] { 076 TokenTypes.LITERAL_WHILE, 077 TokenTypes.LITERAL_TRY, 078 TokenTypes.LITERAL_CATCH, 079 TokenTypes.LITERAL_FINALLY, 080 TokenTypes.LITERAL_DO, 081 TokenTypes.LITERAL_IF, 082 TokenTypes.LITERAL_ELSE, 083 TokenTypes.LITERAL_FOR, 084 TokenTypes.INSTANCE_INIT, 085 TokenTypes.STATIC_INIT, 086 TokenTypes.LITERAL_SWITCH, 087 //TODO: does this handle TokenTypes.LITERAL_SYNCHRONIZED? 088 }; 089 } 090 091 @Override 092 public void visitToken(DetailAST aAST) 093 { 094 final DetailAST slistToken = aAST.findFirstToken(TokenTypes.SLIST); 095 final DetailAST leftCurly = slistToken != null 096 ? slistToken : aAST.findFirstToken(TokenTypes.LCURLY); 097 if (leftCurly != null) { 098 if (getAbstractOption() == BlockOption.STMT) { 099 boolean emptyBlock; 100 if (leftCurly.getType() == TokenTypes.LCURLY) { 101 emptyBlock = leftCurly.getNextSibling().getType() != TokenTypes.CASE_GROUP; 102 } 103 else { 104 emptyBlock = leftCurly.getChildCount() <= 1; 105 } 106 if (emptyBlock) { 107 log(leftCurly.getLineNo(), 108 leftCurly.getColumnNo(), 109 "block.noStmt", 110 aAST.getText()); 111 } 112 } 113 else if (getAbstractOption() == BlockOption.TEXT 114 && !hasText(leftCurly)) 115 { 116 log(leftCurly.getLineNo(), 117 leftCurly.getColumnNo(), 118 "block.empty", 119 aAST.getText()); 120 } 121 } 122 } 123 124 /** 125 * @param aSlistAST a <code>DetailAST</code> value 126 * @return whether the SLIST token contains any text. 127 */ 128 protected boolean hasText(final DetailAST aSlistAST) 129 { 130 boolean retVal = false; 131 132 final DetailAST rightCurly = aSlistAST.findFirstToken(TokenTypes.RCURLY); 133 final DetailAST rcurlyAST = rightCurly != null 134 ? rightCurly : aSlistAST.getParent().findFirstToken(TokenTypes.RCURLY); 135 if (rcurlyAST != null) { 136 final int slistLineNo = aSlistAST.getLineNo(); 137 final int slistColNo = aSlistAST.getColumnNo(); 138 final int rcurlyLineNo = rcurlyAST.getLineNo(); 139 final int rcurlyColNo = rcurlyAST.getColumnNo(); 140 final String[] lines = getLines(); 141 if (slistLineNo == rcurlyLineNo) { 142 // Handle braces on the same line 143 final String txt = lines[slistLineNo - 1] 144 .substring(slistColNo + 1, rcurlyColNo); 145 if (txt.trim().length() != 0) { 146 retVal = true; 147 } 148 } 149 else { 150 // check only whitespace of first & last lines 151 if ((lines[slistLineNo - 1] 152 .substring(slistColNo + 1).trim().length() != 0) 153 || (lines[rcurlyLineNo - 1] 154 .substring(0, rcurlyColNo).trim().length() != 0)) 155 { 156 retVal = true; 157 } 158 else { 159 // check if all lines are also only whitespace 160 for (int i = slistLineNo; i < (rcurlyLineNo - 1); i++) { 161 if (lines[i].trim().length() > 0) { 162 retVal = true; 163 break; 164 } 165 } 166 } 167 } 168 } 169 return retVal; 170 } 171}