Coverage Report - com.puppycrawl.tools.checkstyle.checks.whitespace.SingleSpaceSeparatorCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
SingleSpaceSeparatorCheck
100%
33/33
100%
28/28
1.714
 
 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.whitespace;
 21  
 
 22  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 23  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 24  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 25  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 26  
 
 27  
 /**
 28  
  * <p>
 29  
  * Checks that non-whitespace characters are separated by no more than one
 30  
  * whitespace. Separating characters by tabs or multiple spaces will be
 31  
  * reported. Currently the check doesn't permit horizontal alignment. To inspect
 32  
  * whitespaces before and after comments, set the property
 33  
  * <b>validateComments</b> to true.
 34  
  * </p>
 35  
  *
 36  
  * <p>
 37  
  * Setting <b>validateComments</b> to false will ignore cases like:
 38  
  * </p>
 39  
  *
 40  
  * <pre>
 41  
  * int i;  &#47;&#47; Multiple whitespaces before comment tokens will be ignored.
 42  
  * private void foo(int  &#47;* whitespaces before and after block-comments will be
 43  
  * ignored *&#47;  i) {
 44  
  * </pre>
 45  
  *
 46  
  * <p>
 47  
  * Sometimes, users like to space similar items on different lines to the same
 48  
  * column position for easier reading. This feature isn't supported by this
 49  
  * check, so both braces in the following case will be reported as violations.
 50  
  * </p>
 51  
  *
 52  
  * <pre>
 53  
  * public long toNanos(long d)  { return d;             }  &#47;&#47; 2 violations
 54  
  * public long toMicros(long d) { return d / (C1 / C0); }
 55  
  * </pre>
 56  
  *
 57  
  * <p>
 58  
  * Check have following options:
 59  
  * </p>
 60  
  *
 61  
  * <ul>
 62  
  * <li>validateComments - Boolean when set to {@code true}, whitespaces
 63  
  * surrounding comments will be ignored. Default value is {@code false}.</li>
 64  
  * </ul>
 65  
  *
 66  
  * <p>
 67  
  * To configure the check:
 68  
  * </p>
 69  
  *
 70  
  * <pre>
 71  
  * &lt;module name=&quot;SingleSpaceSeparator&quot;/&gt;
 72  
  * </pre>
 73  
  *
 74  
  * <p>
 75  
  * To configure the check so that it validates comments:
 76  
  * </p>
 77  
  *
 78  
  * <pre>
 79  
  * &lt;module name=&quot;SingleSpaceSeparator&quot;&gt;
 80  
  * &lt;property name=&quot;validateComments&quot; value=&quot;true&quot;/&gt;
 81  
  * &lt;/module&gt;
 82  
  * </pre>
 83  
  *
 84  
  * @author Robert Whitebit
 85  
  * @author Richard Veach
 86  
  */
 87  
 @StatelessCheck
 88  16
 public class SingleSpaceSeparatorCheck extends AbstractCheck {
 89  
     /**
 90  
      * A key is pointing to the warning message text in "messages.properties"
 91  
      * file.
 92  
      */
 93  
     public static final String MSG_KEY = "single.space.separator";
 94  
 
 95  
     /** Indicates if whitespaces surrounding comments will be ignored. */
 96  
     private boolean validateComments;
 97  
 
 98  
     /**
 99  
      * Sets whether or not to validate surrounding whitespaces at comments.
 100  
      *
 101  
      * @param validateComments {@code true} to validate surrounding whitespaces at comments.
 102  
      */
 103  
     public void setValidateComments(boolean validateComments) {
 104  4
         this.validateComments = validateComments;
 105  4
     }
 106  
 
 107  
     @Override
 108  
     public int[] getDefaultTokens() {
 109  14
         return getRequiredTokens();
 110  
     }
 111  
 
 112  
     @Override
 113  
     public int[] getAcceptableTokens() {
 114  5
         return getRequiredTokens();
 115  
     }
 116  
 
 117  
     @Override
 118  
     public int[] getRequiredTokens() {
 119  33
         return CommonUtils.EMPTY_INT_ARRAY;
 120  
     }
 121  
 
 122  
     // -@cs[SimpleAccessorNameNotation] Overrides method from base class.
 123  
     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
 124  
     @Override
 125  
     public boolean isCommentNodesRequired() {
 126  10
         return validateComments;
 127  
     }
 128  
 
 129  
     @Override
 130  
     public void beginTree(DetailAST rootAST) {
 131  8
         visitEachToken(rootAST);
 132  8
     }
 133  
 
 134  
     /**
 135  
      * Examines every sibling and child of {@code node} for violations.
 136  
      *
 137  
      * @param node The node to start examining.
 138  
      */
 139  
     private void visitEachToken(DetailAST node) {
 140  266
         DetailAST sibling = node;
 141  
 
 142  882
         while (sibling != null) {
 143  616
             final int columnNo = sibling.getColumnNo() - 1;
 144  
 
 145  
             // in such expression: "j  =123", placed at the start of the string index of the second
 146  
             // space character will be: 2 = 0(j) + 1(whitespace) + 1(whitespace). It is a minimal
 147  
             // possible index for the second whitespace between non-whitespace characters.
 148  616
             final int minSecondWhitespaceColumnNo = 2;
 149  
 
 150  616
             if (columnNo >= minSecondWhitespaceColumnNo
 151  556
                     && !isTextSeparatedCorrectlyFromPrevious(getLine(sibling.getLineNo() - 1),
 152  
                             columnNo)) {
 153  53
                 log(sibling.getLineNo(), columnNo, MSG_KEY);
 154  
             }
 155  616
             if (sibling.getChildCount() >= 1) {
 156  258
                 visitEachToken(sibling.getFirstChild());
 157  
             }
 158  
 
 159  616
             sibling = sibling.getNextSibling();
 160  616
         }
 161  266
     }
 162  
 
 163  
     /**
 164  
      * Checks if characters in {@code line} at and around {@code columnNo} has
 165  
      * the correct number of spaces. to return {@code true} the following
 166  
      * conditions must be met:<br />
 167  
      * - the character at {@code columnNo} is the first in the line.<br />
 168  
      * - the character at {@code columnNo} is not separated by whitespaces from
 169  
      * the previous non-whitespace character. <br />
 170  
      * - the character at {@code columnNo} is separated by only one whitespace
 171  
      * from the previous non-whitespace character.<br />
 172  
      * - {@link #validateComments} is disabled and the previous text is the
 173  
      * end of a block comment.
 174  
      *
 175  
      * @param line The line in the file to examine.
 176  
      * @param columnNo The column position in the {@code line} to examine.
 177  
      * @return {@code true} if the text at {@code columnNo} is separated
 178  
      *         correctly from the previous token.
 179  
      */
 180  
     private boolean isTextSeparatedCorrectlyFromPrevious(String line, int columnNo) {
 181  1112
         return isSingleSpace(line, columnNo)
 182  415
                 || !isWhitespace(line, columnNo)
 183  164
                 || isFirstInLine(line, columnNo)
 184  5
                 || !validateComments && isBlockCommentEnd(line, columnNo);
 185  
     }
 186  
 
 187  
     /**
 188  
      * Checks if the {@code line} at {@code columnNo} is a single space, and not
 189  
      * preceded by another space.
 190  
      *
 191  
      * @param line The line in the file to examine.
 192  
      * @param columnNo The column position in the {@code line} to examine.
 193  
      * @return {@code true} if the character at {@code columnNo} is a space, and
 194  
      *         not preceded by another space.
 195  
      */
 196  
     private static boolean isSingleSpace(String line, int columnNo) {
 197  1112
         return !isPrecededByMultipleWhitespaces(line, columnNo)
 198  395
                 && isSpace(line, columnNo);
 199  
     }
 200  
 
 201  
     /**
 202  
      * Checks if the {@code line} at {@code columnNo} is a space.
 203  
      *
 204  
      * @param line The line in the file to examine.
 205  
      * @param columnNo The column position in the {@code line} to examine.
 206  
      * @return {@code true} if the character at {@code columnNo} is a space.
 207  
      */
 208  
     private static boolean isSpace(String line, int columnNo) {
 209  395
         return line.charAt(columnNo) == ' ';
 210  
     }
 211  
 
 212  
     /**
 213  
      * Checks if the {@code line} at {@code columnNo} is preceded by at least 2
 214  
      * whitespaces.
 215  
      *
 216  
      * @param line The line in the file to examine.
 217  
      * @param columnNo The column position in the {@code line} to examine.
 218  
      * @return {@code true} if there are at least 2 whitespace characters before
 219  
      *         {@code columnNo}.
 220  
      */
 221  
     private static boolean isPrecededByMultipleWhitespaces(String line, int columnNo) {
 222  1112
         return Character.isWhitespace(line.charAt(columnNo))
 223  305
                 && Character.isWhitespace(line.charAt(columnNo - 1));
 224  
     }
 225  
 
 226  
     /**
 227  
      * Checks if the {@code line} at {@code columnNo} is a whitespace character.
 228  
      *
 229  
      * @param line The line in the file to examine.
 230  
      * @param columnNo The column position in the {@code line} to examine.
 231  
      * @return {@code true} if the character at {@code columnNo} is a
 232  
      *         whitespace.
 233  
      */
 234  
     private static boolean isWhitespace(String line, int columnNo) {
 235  415
         return Character.isWhitespace(line.charAt(columnNo));
 236  
     }
 237  
 
 238  
     /**
 239  
      * Checks if the {@code line} up to and including {@code columnNo} is all
 240  
      * non-whitespace text encountered.
 241  
      *
 242  
      * @param line The line in the file to examine.
 243  
      * @param columnNo The column position in the {@code line} to examine.
 244  
      * @return {@code true} if the column position is the first non-whitespace
 245  
      *         text on the {@code line}.
 246  
      */
 247  
     private static boolean isFirstInLine(String line, int columnNo) {
 248  164
         return CommonUtils.isBlank(line.substring(0, columnNo));
 249  
     }
 250  
 
 251  
     /**
 252  
      * Checks if the {@code line} at {@code columnNo} is the end of a comment,
 253  
      * '*&#47;'.
 254  
      *
 255  
      * @param line The line in the file to examine.
 256  
      * @param columnNo The column position in the {@code line} to examine.
 257  
      * @return {@code true} if the previous text is a end comment block.
 258  
      */
 259  
     private static boolean isBlockCommentEnd(String line, int columnNo) {
 260  5
         return line.substring(0, columnNo).trim().endsWith("*/");
 261  
     }
 262  
 }