View Javadoc
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.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
27  
28  /**
29   * <p>
30   * Checks that a token is followed by whitespace, with the exception that it
31   * does not check for whitespace after the semicolon of an empty for iterator.
32   * Use Check {@link EmptyForIteratorPadCheck EmptyForIteratorPad} to validate
33   * empty for iterators.
34   * </p>
35   * <p> By default the check will check the following tokens:
36   *  {@link TokenTypes#COMMA COMMA},
37   *  {@link TokenTypes#SEMI SEMI},
38   *  {@link TokenTypes#TYPECAST TYPECAST},
39   *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
40   *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
41   *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
42   *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
43   *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
44   *  {@link TokenTypes#DO_WHILE DO_WHILE}.
45   * </p>
46   * <p>
47   * An example of how to configure the check is:
48   * </p>
49   * <pre>
50   * &lt;module name="WhitespaceAfter"/&gt;
51   * </pre>
52   * <p> An example of how to configure the check for whitespace only after
53   * {@link TokenTypes#COMMA COMMA} and {@link TokenTypes#SEMI SEMI} tokens is:
54   * </p>
55   * <pre>
56   * &lt;module name="WhitespaceAfter"&gt;
57   *     &lt;property name="tokens" value="COMMA, SEMI"/&gt;
58   * &lt;/module&gt;
59   * </pre>
60   * @author Oliver Burn
61   * @author Rick Giles
62   */
63  @StatelessCheck
64  public class WhitespaceAfterCheck
65      extends AbstractCheck {
66  
67      /**
68       * A key is pointing to the warning message text in "messages.properties"
69       * file.
70       */
71      public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
72  
73      /**
74       * A key is pointing to the warning message text in "messages.properties"
75       * file.
76       */
77      public static final String MSG_WS_TYPECAST = "ws.typeCast";
78  
79      @Override
80      public int[] getDefaultTokens() {
81          return getAcceptableTokens();
82      }
83  
84      @Override
85      public int[] getAcceptableTokens() {
86          return new int[] {
87              TokenTypes.COMMA,
88              TokenTypes.SEMI,
89              TokenTypes.TYPECAST,
90              TokenTypes.LITERAL_IF,
91              TokenTypes.LITERAL_ELSE,
92              TokenTypes.LITERAL_WHILE,
93              TokenTypes.LITERAL_DO,
94              TokenTypes.LITERAL_FOR,
95              TokenTypes.DO_WHILE,
96          };
97      }
98  
99      @Override
100     public int[] getRequiredTokens() {
101         return CommonUtils.EMPTY_INT_ARRAY;
102     }
103 
104     @Override
105     public void visitToken(DetailAST ast) {
106         if (ast.getType() == TokenTypes.TYPECAST) {
107             final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN);
108             final String line = getLine(targetAST.getLineNo() - 1);
109             if (!isFollowedByWhitespace(targetAST, line)) {
110                 log(targetAST.getLineNo(),
111                     targetAST.getColumnNo() + targetAST.getText().length(),
112                     MSG_WS_TYPECAST);
113             }
114         }
115         else {
116             final String line = getLine(ast.getLineNo() - 1);
117             if (!isFollowedByWhitespace(ast, line)) {
118                 final Object[] message = {ast.getText()};
119                 log(ast.getLineNo(),
120                     ast.getColumnNo() + ast.getText().length(),
121                     MSG_WS_NOT_FOLLOWED,
122                     message);
123             }
124         }
125     }
126 
127     /**
128      * Checks whether token is followed by a whitespace.
129      * @param targetAST Ast token.
130      * @param line The line associated with the ast token.
131      * @return true if ast token is followed by a whitespace.
132      */
133     private static boolean isFollowedByWhitespace(DetailAST targetAST, String line) {
134         final int after =
135             targetAST.getColumnNo() + targetAST.getText().length();
136         boolean followedByWhitespace = true;
137 
138         if (after < line.length()) {
139             final char charAfter = line.charAt(after);
140             followedByWhitespace = charAfter == ';'
141                 || charAfter == ')'
142                 || Character.isWhitespace(charAfter);
143         }
144         return followedByWhitespace;
145     }
146 }