Coverage Report - com.puppycrawl.tools.checkstyle.checks.blocks.EmptyBlockCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
EmptyBlockCheck
100%
64/64
100%
42/42
3.5
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2018 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  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.blocks;
 21  
 
 22  
 import java.util.Locale;
 23  
 
 24  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 25  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 26  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 27  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 28  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 29  
 
 30  
 /**
 31  
  * Checks for empty blocks. This check does not validate sequential blocks.
 32  
  * The policy to verify is specified using the {@link
 33  
  * BlockOption} class and defaults to {@link BlockOption#STATEMENT}.
 34  
  *
 35  
  * <p> By default the check will check the following blocks:
 36  
  *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
 37  
  *  {@link TokenTypes#LITERAL_TRY LITERAL_TRY},
 38  
  *  {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY},
 39  
  *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
 40  
  *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
 41  
  *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
 42  
  *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
 43  
  *  {@link TokenTypes#STATIC_INIT STATIC_INIT},
 44  
  *  {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}.
 45  
  *  {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}.
 46  
  * </p>
 47  
  *
 48  
  * <p> An example of how to configure the check is:
 49  
  * </p>
 50  
  * <pre>
 51  
  * &lt;module name="EmptyBlock"/&gt;
 52  
  * </pre>
 53  
  *
 54  
  * <p> An example of how to configure the check for the {@link
 55  
  * BlockOption#TEXT} policy and only try blocks is:
 56  
  * </p>
 57  
  *
 58  
  * <pre>
 59  
  * &lt;module name="EmptyBlock"&gt;
 60  
  *    &lt;property name="tokens" value="LITERAL_TRY"/&gt;
 61  
  *    &lt;property name="option" value="text"/&gt;
 62  
  * &lt;/module&gt;
 63  
  * </pre>
 64  
  *
 65  
  * @author Lars K├╝hne
 66  
  */
 67  
 @StatelessCheck
 68  23
 public class EmptyBlockCheck
 69  
     extends AbstractCheck {
 70  
 
 71  
     /**
 72  
      * A key is pointing to the warning message text in "messages.properties"
 73  
      * file.
 74  
      */
 75  
     public static final String MSG_KEY_BLOCK_NO_STATEMENT = "block.noStatement";
 76  
 
 77  
     /**
 78  
      * A key is pointing to the warning message text in "messages.properties"
 79  
      * file.
 80  
      */
 81  
     public static final String MSG_KEY_BLOCK_EMPTY = "block.empty";
 82  
 
 83  
     /** The policy to enforce. */
 84  23
     private BlockOption option = BlockOption.STATEMENT;
 85  
 
 86  
     /**
 87  
      * Set the option to enforce.
 88  
      * @param optionStr string to decode option from
 89  
      * @throws IllegalArgumentException if unable to decode
 90  
      */
 91  
     public void setOption(String optionStr) {
 92  
         try {
 93  12
             option = BlockOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
 94  
         }
 95  1
         catch (IllegalArgumentException iae) {
 96  1
             throw new IllegalArgumentException("unable to parse " + optionStr, iae);
 97  11
         }
 98  11
     }
 99  
 
 100  
     @Override
 101  
     public int[] getDefaultTokens() {
 102  10
         return new int[] {
 103  
             TokenTypes.LITERAL_WHILE,
 104  
             TokenTypes.LITERAL_TRY,
 105  
             TokenTypes.LITERAL_FINALLY,
 106  
             TokenTypes.LITERAL_DO,
 107  
             TokenTypes.LITERAL_IF,
 108  
             TokenTypes.LITERAL_ELSE,
 109  
             TokenTypes.LITERAL_FOR,
 110  
             TokenTypes.INSTANCE_INIT,
 111  
             TokenTypes.STATIC_INIT,
 112  
             TokenTypes.LITERAL_SWITCH,
 113  
             TokenTypes.LITERAL_SYNCHRONIZED,
 114  
         };
 115  
     }
 116  
 
 117  
     @Override
 118  
     public int[] getAcceptableTokens() {
 119  16
         return new int[] {
 120  
             TokenTypes.LITERAL_WHILE,
 121  
             TokenTypes.LITERAL_TRY,
 122  
             TokenTypes.LITERAL_CATCH,
 123  
             TokenTypes.LITERAL_FINALLY,
 124  
             TokenTypes.LITERAL_DO,
 125  
             TokenTypes.LITERAL_IF,
 126  
             TokenTypes.LITERAL_ELSE,
 127  
             TokenTypes.LITERAL_FOR,
 128  
             TokenTypes.INSTANCE_INIT,
 129  
             TokenTypes.STATIC_INIT,
 130  
             TokenTypes.LITERAL_SWITCH,
 131  
             TokenTypes.LITERAL_SYNCHRONIZED,
 132  
             TokenTypes.LITERAL_CASE,
 133  
             TokenTypes.LITERAL_DEFAULT,
 134  
             TokenTypes.ARRAY_INIT,
 135  
         };
 136  
     }
 137  
 
 138  
     @Override
 139  
     public int[] getRequiredTokens() {
 140  33
         return CommonUtils.EMPTY_INT_ARRAY;
 141  
     }
 142  
 
 143  
     @Override
 144  
     public void visitToken(DetailAST ast) {
 145  114
         final DetailAST leftCurly = findLeftCurly(ast);
 146  114
         if (leftCurly != null) {
 147  86
             if (option == BlockOption.STATEMENT) {
 148  
                 final boolean emptyBlock;
 149  50
                 if (leftCurly.getType() == TokenTypes.LCURLY) {
 150  2
                     emptyBlock = leftCurly.getNextSibling().getType() != TokenTypes.CASE_GROUP;
 151  
                 }
 152  
                 else {
 153  48
                     emptyBlock = leftCurly.getChildCount() <= 1;
 154  
                 }
 155  50
                 if (emptyBlock) {
 156  72
                     log(leftCurly.getLineNo(),
 157  36
                         leftCurly.getColumnNo(),
 158  
                         MSG_KEY_BLOCK_NO_STATEMENT,
 159  36
                         ast.getText());
 160  
                 }
 161  50
             }
 162  36
             else if (!hasText(leftCurly)) {
 163  40
                 log(leftCurly.getLineNo(),
 164  20
                     leftCurly.getColumnNo(),
 165  
                     MSG_KEY_BLOCK_EMPTY,
 166  20
                     ast.getText());
 167  
             }
 168  
         }
 169  114
     }
 170  
 
 171  
     /**
 172  
      * Checks if SLIST token contains any text.
 173  
      * @param slistAST a {@code DetailAST} value
 174  
      * @return whether the SLIST token contains any text.
 175  
      */
 176  
     private boolean hasText(final DetailAST slistAST) {
 177  36
         final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY);
 178  
         final DetailAST rcurlyAST;
 179  
 
 180  36
         if (rightCurly == null) {
 181  2
             rcurlyAST = slistAST.getParent().findFirstToken(TokenTypes.RCURLY);
 182  
         }
 183  
         else {
 184  34
             rcurlyAST = rightCurly;
 185  
         }
 186  36
         final int slistLineNo = slistAST.getLineNo();
 187  36
         final int slistColNo = slistAST.getColumnNo();
 188  36
         final int rcurlyLineNo = rcurlyAST.getLineNo();
 189  36
         final int rcurlyColNo = rcurlyAST.getColumnNo();
 190  36
         final String[] lines = getLines();
 191  36
         boolean returnValue = false;
 192  36
         if (slistLineNo == rcurlyLineNo) {
 193  
             // Handle braces on the same line
 194  16
             final String txt = lines[slistLineNo - 1]
 195  16
                     .substring(slistColNo + 1, rcurlyColNo);
 196  16
             if (!CommonUtils.isBlank(txt)) {
 197  1
                 returnValue = true;
 198  
             }
 199  16
         }
 200  
         else {
 201  20
             final String firstLine = lines[slistLineNo - 1].substring(slistColNo + 1);
 202  20
             final String lastLine = lines[rcurlyLineNo - 1].substring(0, rcurlyColNo);
 203  
             // check if all lines are also only whitespace
 204  20
             returnValue = !(CommonUtils.isBlank(firstLine) && CommonUtils.isBlank(lastLine))
 205  12
                     || !checkIsAllLinesAreWhitespace(lines, slistLineNo, rcurlyLineNo);
 206  
         }
 207  36
         return returnValue;
 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  12
         boolean result = true;
 223  13
         for (int i = lineFrom; i < lineTo - 1; i++) {
 224  8
             if (!CommonUtils.isBlank(lines[i])) {
 225  7
                 result = false;
 226  7
                 break;
 227  
             }
 228  
         }
 229  12
         return result;
 230  
     }
 231  
 
 232  
     /**
 233  
      * Calculates the left curly corresponding to the block to be checked.
 234  
      *
 235  
      * @param ast a {@code DetailAST} value
 236  
      * @return the left curly corresponding to the block to be checked
 237  
      */
 238  
     private static DetailAST findLeftCurly(DetailAST ast) {
 239  
         final DetailAST leftCurly;
 240  114
         final DetailAST slistAST = ast.findFirstToken(TokenTypes.SLIST);
 241  114
         if ((ast.getType() == TokenTypes.LITERAL_CASE
 242  90
                 || ast.getType() == TokenTypes.LITERAL_DEFAULT)
 243  57
                 && ast.getNextSibling() != null
 244  49
                 && ast.getNextSibling().getFirstChild() != null
 245  48
                 && ast.getNextSibling().getFirstChild().getType() == TokenTypes.SLIST) {
 246  32
             leftCurly = ast.getNextSibling().getFirstChild();
 247  
         }
 248  82
         else if (slistAST == null) {
 249  32
             leftCurly = ast.findFirstToken(TokenTypes.LCURLY);
 250  
         }
 251  
         else {
 252  50
             leftCurly = slistAST;
 253  
         }
 254  114
         return leftCurly;
 255  
     }
 256  
 
 257  
 }