View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2015 the original author or authors.
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_FINALLY LITERAL_FINALLY},
33   *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
34   *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
35   *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
36   *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
37   *  {@link TokenTypes#STATIC_INIT STATIC_INIT},
38   *  {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}.
39   * </p>
40   *
41   * <p> An example of how to configure the check is:
42   * </p>
43   * <pre>
44   * &lt;module name="EmptyBlock"/&gt;
45   * </pre>
46   *
47   * <p> An example of how to configure the check for the {@link
48   * BlockOption#TEXT} policy and only try blocks is:
49   * </p>
50   *
51   * <pre>
52   * &lt;module name="EmptyBlock"&gt;
53   *    &lt;property name="tokens" value="LITERAL_TRY"/&gt;
54   *    &lt;property name="option" value="text"/&gt;
55   * &lt;/module&gt;
56   * </pre>
57   *
58   * @author Lars Kühne
59   */
60  public class EmptyBlockCheck
61      extends AbstractOptionCheck<BlockOption>
62  {
63      /**
64       * A key is pointing to the warning message text in "messages.properties"
65       * file.
66       */
67      public static final String MSG_KEY_BLOCK_NO_STMT = "block.noStmt";
68  
69      /**
70       * A key is pointing to the warning message text in "messages.properties"
71       * file.
72       */
73      public static final String MSG_KEY_BLOCK_EMPTY = "block.empty";
74  
75      /**
76       * Creates a new <code>EmptyBlockCheck</code> instance.
77       */
78      public EmptyBlockCheck()
79      {
80          super(BlockOption.STMT, BlockOption.class);
81      }
82  
83      @Override
84      public int[] getDefaultTokens()
85      {
86          return new int[] {
87              TokenTypes.LITERAL_WHILE,
88              TokenTypes.LITERAL_TRY,
89              TokenTypes.LITERAL_FINALLY,
90              TokenTypes.LITERAL_DO,
91              TokenTypes.LITERAL_IF,
92              TokenTypes.LITERAL_ELSE,
93              TokenTypes.LITERAL_FOR,
94              TokenTypes.INSTANCE_INIT,
95              TokenTypes.STATIC_INIT,
96              TokenTypes.LITERAL_SWITCH,
97              //TODO: does this handle TokenTypes.LITERAL_SYNCHRONIZED?
98          };
99      }
100 
101     @Override
102     public int[] getAcceptableTokens()
103     {
104         return new int[] {
105             TokenTypes.LITERAL_WHILE,
106             TokenTypes.LITERAL_TRY,
107             TokenTypes.LITERAL_CATCH,
108             TokenTypes.LITERAL_FINALLY,
109             TokenTypes.LITERAL_DO,
110             TokenTypes.LITERAL_IF,
111             TokenTypes.LITERAL_ELSE,
112             TokenTypes.LITERAL_FOR,
113             TokenTypes.INSTANCE_INIT,
114             TokenTypes.STATIC_INIT,
115             TokenTypes.LITERAL_SWITCH,
116             TokenTypes.LITERAL_CASE,
117             TokenTypes.LITERAL_SWITCH,
118             TokenTypes.LITERAL_DEFAULT,
119             TokenTypes.ARRAY_INIT,
120             //TODO: does this handle TokenTypes.LITERAL_SYNCHRONIZED?
121         };
122     }
123 
124     @Override
125     public void visitToken(DetailAST ast)
126     {
127         final DetailAST slistToken = ast.findFirstToken(TokenTypes.SLIST);
128         final DetailAST leftCurly = slistToken != null
129                 ? slistToken : ast.findFirstToken(TokenTypes.LCURLY);
130         if (leftCurly != null) {
131             if (getAbstractOption() == BlockOption.STMT) {
132                 boolean emptyBlock;
133                 if (leftCurly.getType() == TokenTypes.LCURLY) {
134                     emptyBlock = leftCurly.getNextSibling().getType() != TokenTypes.CASE_GROUP;
135                 }
136                 else {
137                     emptyBlock = leftCurly.getChildCount() <= 1;
138                 }
139                 if (emptyBlock) {
140                     log(leftCurly.getLineNo(),
141                         leftCurly.getColumnNo(),
142                         MSG_KEY_BLOCK_NO_STMT,
143                         ast.getText());
144                 }
145             }
146             else if (getAbstractOption() == BlockOption.TEXT
147                     && !hasText(leftCurly))
148             {
149                 log(leftCurly.getLineNo(),
150                     leftCurly.getColumnNo(),
151                     MSG_KEY_BLOCK_EMPTY,
152                     ast.getText());
153             }
154         }
155     }
156 
157     /**
158      * @param slistAST a <code>DetailAST</code> value
159      * @return whether the SLIST token contains any text.
160      */
161     protected boolean hasText(final DetailAST slistAST)
162     {
163         boolean retVal = false;
164 
165         final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY);
166         final DetailAST rcurlyAST = rightCurly != null
167                 ? rightCurly : slistAST.getParent().findFirstToken(TokenTypes.RCURLY);
168         if (rcurlyAST != null) {
169             final int slistLineNo = slistAST.getLineNo();
170             final int slistColNo = slistAST.getColumnNo();
171             final int rcurlyLineNo = rcurlyAST.getLineNo();
172             final int rcurlyColNo = rcurlyAST.getColumnNo();
173             final String[] lines = getLines();
174             if (slistLineNo == rcurlyLineNo) {
175                 // Handle braces on the same line
176                 final String txt = lines[slistLineNo - 1]
177                     .substring(slistColNo + 1, rcurlyColNo);
178                 if (txt.trim().length() != 0) {
179                     retVal = true;
180                 }
181             }
182             else {
183                 // check only whitespace of first & last lines
184                 if (lines[slistLineNo - 1]
185                      .substring(slistColNo + 1).trim().length() != 0
186                     || lines[rcurlyLineNo - 1]
187                         .substring(0, rcurlyColNo).trim().length() != 0)
188                 {
189                     retVal = true;
190                 }
191                 else {
192                     // check if all lines are also only whitespace
193                     for (int i = slistLineNo; i < rcurlyLineNo - 1; i++) {
194                         if (lines[i].trim().length() > 0) {
195                             retVal = true;
196                             break;
197                         }
198                     }
199                 }
200             }
201         }
202         return retVal;
203     }
204 }