001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.whitespace;
021
022import org.apache.commons.lang3.ArrayUtils;
023
024import com.puppycrawl.tools.checkstyle.api.Check;
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027
028/**
029 * <p>
030 * Checks that a token is followed by whitespace, with the exception that it
031 * does not check for whitespace after the semicolon of an empty for iterator.
032 * Use Check {@link EmptyForIteratorPadCheck EmptyForIteratorPad} to validate
033 * empty for iterators.
034 * </p>
035 * <p> By default the check will check the following tokens:
036 *  {@link TokenTypes#COMMA COMMA},
037 *  {@link TokenTypes#SEMI SEMI},
038 *  {@link TokenTypes#TYPECAST TYPECAST}.
039 * </p>
040 * <p>
041 * An example of how to configure the check is:
042 * </p>
043 * <pre>
044 * &lt;module name="WhitespaceAfter"/&gt;
045 * </pre>
046 * <p> An example of how to configure the check for whitespace only after
047 * {@link TokenTypes#COMMA COMMA} and {@link TokenTypes#SEMI SEMI} tokens is:
048 * </p>
049 * <pre>
050 * &lt;module name="WhitespaceAfter"&gt;
051 *     &lt;property name="tokens" value="COMMA, SEMI"/&gt;
052 * &lt;/module&gt;
053 * </pre>
054 * @author Oliver Burn
055 * @author Rick Giles
056 */
057public class WhitespaceAfterCheck
058    extends Check {
059
060    /**
061     * A key is pointing to the warning message text in "messages.properties"
062     * file.
063     */
064    public static final String WS_NOT_FOLLOWED = "ws.notFollowed";
065
066    /**
067     * A key is pointing to the warning message text in "messages.properties"
068     * file.
069     */
070    public static final String WS_TYPECAST = "ws.typeCast";
071
072    @Override
073    public int[] getDefaultTokens() {
074        return getAcceptableTokens();
075    }
076
077    @Override
078    public int[] getAcceptableTokens() {
079        return new int[] {
080            TokenTypes.COMMA,
081            TokenTypes.SEMI,
082            TokenTypes.TYPECAST,
083        };
084    }
085
086    @Override
087    public int[] getRequiredTokens() {
088        return ArrayUtils.EMPTY_INT_ARRAY;
089    }
090
091    @Override
092    public void visitToken(DetailAST ast) {
093        final String line = getLine(ast.getLineNo() - 1);
094        if (ast.getType() == TokenTypes.TYPECAST) {
095            final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN);
096            if (!isFollowedByWhitespace(targetAST, line)) {
097                log(targetAST.getLineNo(),
098                    targetAST.getColumnNo() + targetAST.getText().length(),
099                    WS_TYPECAST);
100            }
101        }
102        else {
103            if (!isFollowedByWhitespace(ast, line)) {
104                final Object[] message = {ast.getText()};
105                log(ast.getLineNo(),
106                    ast.getColumnNo() + ast.getText().length(),
107                    WS_NOT_FOLLOWED,
108                    message);
109            }
110        }
111    }
112
113    /**
114     * Checks whether token is followed by a whitespace.
115     * @param targetAST Ast token.
116     * @param line The line associated with the ast token.
117     * @return true if ast token is followed by a whitespace.
118     */
119    private static boolean isFollowedByWhitespace(DetailAST targetAST, String line) {
120        final int after =
121            targetAST.getColumnNo() + targetAST.getText().length();
122        boolean followedByWhitespace = true;
123
124        if (after < line.length()) {
125            final char charAfter = line.charAt(after);
126            followedByWhitespace = Character.isWhitespace(charAfter)
127                || targetAST.getType() == TokenTypes.SEMI
128                    && (charAfter == ';' || charAfter == ')');
129        }
130        return followedByWhitespace;
131    }
132}