1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2014 Oliver Burn
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ////////////////////////////////////////////////////////////////////////////////
19 package com.puppycrawl.tools.checkstyle.checks.blocks;
20
21 import com.puppycrawl.tools.checkstyle.api.DetailAST;
22 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
23 import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck;
24
25 /**
26 * Checks for empty blocks. The policy to verify is specified using the {@link
27 * BlockOption} class and defaults to {@link BlockOption#STMT}.
28 *
29 * <p> By default the check will check the following blocks:
30 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
31 * {@link TokenTypes#LITERAL_TRY LITERAL_TRY},
32 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
33 * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY},
34 * {@link TokenTypes#LITERAL_DO LITERAL_DO},
35 * {@link TokenTypes#LITERAL_IF LITERAL_IF},
36 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
37 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
38 * {@link TokenTypes#STATIC_INIT STATIC_INIT},
39 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}.
40 * </p>
41 *
42 * <p> An example of how to configure the check is:
43 * </p>
44 * <pre>
45 * <module name="EmptyBlock"/>
46 * </pre>
47 *
48 * <p> An example of how to configure the check for the {@link
49 * BlockOption#TEXT} policy and only catch blocks is:
50 * </p>
51 *
52 * <pre>
53 * <module name="EmptyBlock">
54 * <property name="tokens" value="LITERAL_CATCH"/>
55 * <property name="option" value="text"/>
56 * </module>
57 * </pre>
58 *
59 * @author Lars Kühne
60 */
61 public class EmptyBlockCheck
62 extends AbstractOptionCheck<BlockOption>
63 {
64 /**
65 * A key is pointing to the warning message text in "messages.properties"
66 * file.
67 */
68 public static final String MSG_KEY_BLOCK_NO_STMT = "block.noStmt";
69
70 /**
71 * A key is pointing to the warning message text in "messages.properties"
72 * file.
73 */
74 public static final String MSG_KEY_BLOCK_EMPTY = "block.empty";
75
76 /**
77 * Creates a new <code>EmptyBlockCheck</code> instance.
78 */
79 public EmptyBlockCheck()
80 {
81 super(BlockOption.STMT, BlockOption.class);
82 }
83
84 @Override
85 public int[] getDefaultTokens()
86 {
87 return new int[] {
88 TokenTypes.LITERAL_WHILE,
89 TokenTypes.LITERAL_TRY,
90 TokenTypes.LITERAL_CATCH,
91 TokenTypes.LITERAL_FINALLY,
92 TokenTypes.LITERAL_DO,
93 TokenTypes.LITERAL_IF,
94 TokenTypes.LITERAL_ELSE,
95 TokenTypes.LITERAL_FOR,
96 TokenTypes.INSTANCE_INIT,
97 TokenTypes.STATIC_INIT,
98 TokenTypes.LITERAL_SWITCH,
99 //TODO: does this handle TokenTypes.LITERAL_SYNCHRONIZED?
100 };
101 }
102
103 @Override
104 public void visitToken(DetailAST ast)
105 {
106 final DetailAST slistToken = ast.findFirstToken(TokenTypes.SLIST);
107 final DetailAST leftCurly = slistToken != null
108 ? slistToken : ast.findFirstToken(TokenTypes.LCURLY);
109 if (leftCurly != null) {
110 if (getAbstractOption() == BlockOption.STMT) {
111 boolean emptyBlock;
112 if (leftCurly.getType() == TokenTypes.LCURLY) {
113 emptyBlock = leftCurly.getNextSibling().getType() != TokenTypes.CASE_GROUP;
114 }
115 else {
116 emptyBlock = leftCurly.getChildCount() <= 1;
117 }
118 if (emptyBlock) {
119 log(leftCurly.getLineNo(),
120 leftCurly.getColumnNo(),
121 MSG_KEY_BLOCK_NO_STMT,
122 ast.getText());
123 }
124 }
125 else if (getAbstractOption() == BlockOption.TEXT
126 && !hasText(leftCurly))
127 {
128 log(leftCurly.getLineNo(),
129 leftCurly.getColumnNo(),
130 MSG_KEY_BLOCK_EMPTY,
131 ast.getText());
132 }
133 }
134 }
135
136 /**
137 * @param slistAST a <code>DetailAST</code> value
138 * @return whether the SLIST token contains any text.
139 */
140 protected boolean hasText(final DetailAST slistAST)
141 {
142 boolean retVal = false;
143
144 final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY);
145 final DetailAST rcurlyAST = rightCurly != null
146 ? rightCurly : slistAST.getParent().findFirstToken(TokenTypes.RCURLY);
147 if (rcurlyAST != null) {
148 final int slistLineNo = slistAST.getLineNo();
149 final int slistColNo = slistAST.getColumnNo();
150 final int rcurlyLineNo = rcurlyAST.getLineNo();
151 final int rcurlyColNo = rcurlyAST.getColumnNo();
152 final String[] lines = getLines();
153 if (slistLineNo == rcurlyLineNo) {
154 // Handle braces on the same line
155 final String txt = lines[slistLineNo - 1]
156 .substring(slistColNo + 1, rcurlyColNo);
157 if (txt.trim().length() != 0) {
158 retVal = true;
159 }
160 }
161 else {
162 // check only whitespace of first & last lines
163 if ((lines[slistLineNo - 1]
164 .substring(slistColNo + 1).trim().length() != 0)
165 || (lines[rcurlyLineNo - 1]
166 .substring(0, rcurlyColNo).trim().length() != 0))
167 {
168 retVal = true;
169 }
170 else {
171 // check if all lines are also only whitespace
172 for (int i = slistLineNo; i < (rcurlyLineNo - 1); i++) {
173 if (lines[i].trim().length() > 0) {
174 retVal = true;
175 break;
176 }
177 }
178 }
179 }
180 }
181 return retVal;
182 }
183 }