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-2017 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  
      * A key is pointing to the warning message text in "messages.properties"
 72  
      * file.
 73  
      */
 74  
     public static final String MSG_KEY_BLOCK_NO_STATEMENT = "block.noStatement";
 75  
 
 76  
     /**
 77  
      * A key is pointing to the warning message text in "messages.properties"
 78  
      * file.
 79  
      */
 80  
     public static final String MSG_KEY_BLOCK_EMPTY = "block.empty";
 81  
 
 82  
     /** The policy to enforce. */
 83  23
     private BlockOption option = BlockOption.STATEMENT;
 84  
 
 85  
     /**
 86  
      * Set the option to enforce.
 87  
      * @param optionStr string to decode option from
 88  
      * @throws IllegalArgumentException if unable to decode
 89  
      */
 90  
     public void setOption(String optionStr) {
 91  
         try {
 92  12
             option = BlockOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
 93  
         }
 94  1
         catch (IllegalArgumentException iae) {
 95  1
             throw new IllegalArgumentException("unable to parse " + optionStr, iae);
 96  11
         }
 97  11
     }
 98  
 
 99  
     @Override
 100  
     public int[] getDefaultTokens() {
 101  10
         return new int[] {
 102  
             TokenTypes.LITERAL_WHILE,
 103  
             TokenTypes.LITERAL_TRY,
 104  
             TokenTypes.LITERAL_FINALLY,
 105  
             TokenTypes.LITERAL_DO,
 106  
             TokenTypes.LITERAL_IF,
 107  
             TokenTypes.LITERAL_ELSE,
 108  
             TokenTypes.LITERAL_FOR,
 109  
             TokenTypes.INSTANCE_INIT,
 110  
             TokenTypes.STATIC_INIT,
 111  
             TokenTypes.LITERAL_SWITCH,
 112  
             TokenTypes.LITERAL_SYNCHRONIZED,
 113  
         };
 114  
     }
 115  
 
 116  
     @Override
 117  
     public int[] getAcceptableTokens() {
 118  16
         return new int[] {
 119  
             TokenTypes.LITERAL_WHILE,
 120  
             TokenTypes.LITERAL_TRY,
 121  
             TokenTypes.LITERAL_CATCH,
 122  
             TokenTypes.LITERAL_FINALLY,
 123  
             TokenTypes.LITERAL_DO,
 124  
             TokenTypes.LITERAL_IF,
 125  
             TokenTypes.LITERAL_ELSE,
 126  
             TokenTypes.LITERAL_FOR,
 127  
             TokenTypes.INSTANCE_INIT,
 128  
             TokenTypes.STATIC_INIT,
 129  
             TokenTypes.LITERAL_SWITCH,
 130  
             TokenTypes.LITERAL_SYNCHRONIZED,
 131  
             TokenTypes.LITERAL_CASE,
 132  
             TokenTypes.LITERAL_DEFAULT,
 133  
             TokenTypes.ARRAY_INIT,
 134  
         };
 135  
     }
 136  
 
 137  
     @Override
 138  
     public int[] getRequiredTokens() {
 139  33
         return CommonUtils.EMPTY_INT_ARRAY;
 140  
     }
 141  
 
 142  
     @Override
 143  
     public void visitToken(DetailAST ast) {
 144  114
         final DetailAST leftCurly = findLeftCurly(ast);
 145  114
         if (leftCurly != null) {
 146  86
             if (option == BlockOption.STATEMENT) {
 147  
                 final boolean emptyBlock;
 148  50
                 if (leftCurly.getType() == TokenTypes.LCURLY) {
 149  2
                     emptyBlock = leftCurly.getNextSibling().getType() != TokenTypes.CASE_GROUP;
 150  
                 }
 151  
                 else {
 152  48
                     emptyBlock = leftCurly.getChildCount() <= 1;
 153  
                 }
 154  50
                 if (emptyBlock) {
 155  72
                     log(leftCurly.getLineNo(),
 156  36
                         leftCurly.getColumnNo(),
 157  
                         MSG_KEY_BLOCK_NO_STATEMENT,
 158  36
                         ast.getText());
 159  
                 }
 160  50
             }
 161  36
             else if (!hasText(leftCurly)) {
 162  40
                 log(leftCurly.getLineNo(),
 163  20
                     leftCurly.getColumnNo(),
 164  
                     MSG_KEY_BLOCK_EMPTY,
 165  20
                     ast.getText());
 166  
             }
 167  
         }
 168  114
     }
 169  
 
 170  
     /**
 171  
      * Checks if SLIST token contains any text.
 172  
      * @param slistAST a {@code DetailAST} value
 173  
      * @return whether the SLIST token contains any text.
 174  
      */
 175  
     private boolean hasText(final DetailAST slistAST) {
 176  36
         final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY);
 177  
         final DetailAST rcurlyAST;
 178  
 
 179  36
         if (rightCurly == null) {
 180  2
             rcurlyAST = slistAST.getParent().findFirstToken(TokenTypes.RCURLY);
 181  
         }
 182  
         else {
 183  34
             rcurlyAST = rightCurly;
 184  
         }
 185  36
         final int slistLineNo = slistAST.getLineNo();
 186  36
         final int slistColNo = slistAST.getColumnNo();
 187  36
         final int rcurlyLineNo = rcurlyAST.getLineNo();
 188  36
         final int rcurlyColNo = rcurlyAST.getColumnNo();
 189  36
         final String[] lines = getLines();
 190  36
         boolean returnValue = false;
 191  36
         if (slistLineNo == rcurlyLineNo) {
 192  
             // Handle braces on the same line
 193  16
             final String txt = lines[slistLineNo - 1]
 194  16
                     .substring(slistColNo + 1, rcurlyColNo);
 195  16
             if (!CommonUtils.isBlank(txt)) {
 196  1
                 returnValue = true;
 197  
             }
 198  16
         }
 199  
         else {
 200  20
             final String firstLine = lines[slistLineNo - 1].substring(slistColNo + 1);
 201  20
             final String lastLine = lines[rcurlyLineNo - 1].substring(0, rcurlyColNo);
 202  
             // check if all lines are also only whitespace
 203  20
             returnValue = !(CommonUtils.isBlank(firstLine) && CommonUtils.isBlank(lastLine))
 204  12
                     || !checkIsAllLinesAreWhitespace(lines, slistLineNo, rcurlyLineNo);
 205  
         }
 206  36
         return returnValue;
 207  
     }
 208  
 
 209  
     /**
 210  
      * Checks is all lines in array contain whitespaces only.
 211  
      *
 212  
      * @param lines
 213  
      *            array of lines
 214  
      * @param lineFrom
 215  
      *            check from this line number
 216  
      * @param lineTo
 217  
      *            check to this line numbers
 218  
      * @return true if lines contain only whitespaces
 219  
      */
 220  
     private static boolean checkIsAllLinesAreWhitespace(String[] lines, int lineFrom, int lineTo) {
 221  12
         boolean result = true;
 222  13
         for (int i = lineFrom; i < lineTo - 1; i++) {
 223  8
             if (!CommonUtils.isBlank(lines[i])) {
 224  7
                 result = false;
 225  7
                 break;
 226  
             }
 227  
         }
 228  12
         return result;
 229  
     }
 230  
 
 231  
     /**
 232  
      * Calculates the left curly corresponding to the block to be checked.
 233  
      *
 234  
      * @param ast a {@code DetailAST} value
 235  
      * @return the left curly corresponding to the block to be checked
 236  
      */
 237  
     private static DetailAST findLeftCurly(DetailAST ast) {
 238  
         final DetailAST leftCurly;
 239  114
         final DetailAST slistAST = ast.findFirstToken(TokenTypes.SLIST);
 240  114
         if ((ast.getType() == TokenTypes.LITERAL_CASE
 241  90
                 || ast.getType() == TokenTypes.LITERAL_DEFAULT)
 242  57
                 && ast.getNextSibling() != null
 243  49
                 && ast.getNextSibling().getFirstChild() != null
 244  48
                 && ast.getNextSibling().getFirstChild().getType() == TokenTypes.SLIST) {
 245  32
             leftCurly = ast.getNextSibling().getFirstChild();
 246  
         }
 247  82
         else if (slistAST == null) {
 248  32
             leftCurly = ast.findFirstToken(TokenTypes.LCURLY);
 249  
         }
 250  
         else {
 251  50
             leftCurly = slistAST;
 252  
         }
 253  114
         return leftCurly;
 254  
     }
 255  
 }