001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027
028/**
029 * <p>
030 * Checks that there is no whitespace before a token.
031 * More specifically, it checks that it is not preceded with whitespace,
032 * or (if linebreaks are allowed) all characters on the line before are
033 * whitespace. To allow linebreaks before a token, set property
034 * {@code allowLineBreaks} to {@code true}. No check occurs before semi-colons in empty
035 * for loop initializers or conditions.
036 * </p>
037 * <ul>
038 * <li>
039 * Property {@code allowLineBreaks} - Control whether whitespace is allowed
040 * if the token is at a linebreak.
041 * Type is {@code boolean}.
042 * Default value is {@code false}.
043 * </li>
044 * <li>
045 * Property {@code tokens} - tokens to check
046 * Type is {@code int[]}.
047 * Default value is:
048 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMMA">
049 * COMMA</a>,
050 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SEMI">
051 * SEMI</a>,
052 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_INC">
053 * POST_INC</a>,
054 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_DEC">
055 * POST_DEC</a>,
056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ELLIPSIS">
057 * ELLIPSIS</a>,
058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LABELED_STAT">
059 * LABELED_STAT</a>.
060 * </li>
061 * </ul>
062 * <p>
063 * To configure the check:
064 * </p>
065 * <pre>
066 * &lt;module name=&quot;NoWhitespaceBefore&quot;/&gt;
067 * </pre>
068 * <p>Example:</p>
069 * <pre>
070 * int foo;
071 * foo ++; // violation, whitespace before '++' is not allowed
072 * foo++; // OK
073 * for (int i = 0 ; i &lt; 5; i++) {}  // violation
074 *            // ^ whitespace before ';' is not allowed
075 * for (int i = 0; i &lt; 5; i++) {} // OK
076 * int[][] array = { { 1, 2 }
077 *                 , { 3, 4 } }; // violation, whitespace before ',' is not allowed
078 * int[][] array2 = { { 1, 2 },
079 *                    { 3, 4 } }; // OK
080 * Lists.charactersOf("foo").listIterator()
081 *        .forEachRemaining(System.out::print)
082 *        ; // violation, whitespace before ';' is not allowed
083 *   {
084 *     label1 : // violation, whitespace before ':' is not allowed
085 *     for (int i = 0; i &lt; 10; i++) {}
086 *   }
087 *
088 *   {
089 *     label2: // OK
090 *     while (true) {}
091 *   }
092 * </pre>
093 * <p>To configure the check to allow linebreaks before default tokens:</p>
094 * <pre>
095 * &lt;module name=&quot;NoWhitespaceBefore&quot;&gt;
096 *   &lt;property name=&quot;allowLineBreaks&quot; value=&quot;true&quot;/&gt;
097 * &lt;/module&gt;
098 * </pre>
099 * <p>Example:</p>
100 * <pre>
101 * int[][] array = { { 1, 2 }
102 *                 , { 3, 4 } }; // OK, linebreak is allowed before ','
103 * int[][] array2 = { { 1, 2 },
104 *                    { 3, 4 } }; // OK, ideal code
105 * void ellipsisExample(String ...params) {}; // violation, whitespace before '...' is not allowed
106 * void ellipsisExample2(String
107 *                         ...params) {}; //OK, linebreak is allowed before '...'
108 * Lists.charactersOf("foo")
109 *        .listIterator()
110 *        .forEachRemaining(System.out::print); // OK
111 * </pre>
112 * <p>
113 *     To Configure the check to restrict the use of whitespace before METHOD_REF and DOT tokens:
114 * </p>
115 * <pre>
116 * &lt;module name=&quot;NoWhitespaceBefore&quot;&gt;
117 *   &lt;property name=&quot;tokens&quot; value=&quot;METHOD_REF&quot;/&gt;
118 *   &lt;property name=&quot;tokens&quot; value=&quot;DOT&quot;/&gt;
119 * &lt;/module&gt;
120 * </pre>
121 * <p>Example:</p>
122 * <pre>
123 * Lists.charactersOf("foo").listIterator()
124 *        .forEachRemaining(System.out::print); // violation, whitespace before '.' is not allowed
125 * Lists.charactersOf("foo").listIterator().forEachRemaining(System.out ::print); // violation,
126 *                           // whitespace before '::' is not allowed  ^
127 * Lists.charactersOf("foo").listIterator().forEachRemaining(System.out::print); // OK
128 * </pre>
129 * <p>
130 *     To configure the check to allow linebreak before METHOD_REF and DOT tokens:
131 * </p>
132 * <pre>
133 * &lt;module name=&quot;NoWhitespaceBefore&quot;&gt;
134 *   &lt;property name=&quot;tokens&quot; value=&quot;METHOD_REF&quot;/&gt;
135 *   &lt;property name=&quot;tokens&quot; value=&quot;DOT&quot;/&gt;
136 *   &lt;property name=&quot;allowLineBreaks&quot; value=&quot;true&quot;/&gt;
137 * &lt;/module&gt;
138 * </pre>
139 * <p>Example:</p>
140 * <pre>
141 * Lists .charactersOf("foo") //violation, whitespace before '.' is not allowed
142 *         .listIterator()
143 *         .forEachRemaining(System.out ::print); // violation,
144 *                                  // ^ whitespace before '::' is not allowed
145 * Lists.charactersOf("foo")
146 *        .listIterator()
147 *        .forEachRemaining(System.out::print); // OK
148 * </pre>
149 * <p>
150 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
151 * </p>
152 * <p>
153 * Violation Message Keys:
154 * </p>
155 * <ul>
156 * <li>
157 * {@code ws.preceded}
158 * </li>
159 * </ul>
160 *
161 * @since 3.0
162 */
163@StatelessCheck
164public class NoWhitespaceBeforeCheck
165    extends AbstractCheck {
166
167    /**
168     * A key is pointing to the warning message text in "messages.properties"
169     * file.
170     */
171    public static final String MSG_KEY = "ws.preceded";
172
173    /** Control whether whitespace is allowed if the token is at a linebreak. */
174    private boolean allowLineBreaks;
175
176    @Override
177    public int[] getDefaultTokens() {
178        return new int[] {
179            TokenTypes.COMMA,
180            TokenTypes.SEMI,
181            TokenTypes.POST_INC,
182            TokenTypes.POST_DEC,
183            TokenTypes.ELLIPSIS,
184            TokenTypes.LABELED_STAT,
185        };
186    }
187
188    @Override
189    public int[] getAcceptableTokens() {
190        return new int[] {
191            TokenTypes.COMMA,
192            TokenTypes.SEMI,
193            TokenTypes.POST_INC,
194            TokenTypes.POST_DEC,
195            TokenTypes.DOT,
196            TokenTypes.GENERIC_START,
197            TokenTypes.GENERIC_END,
198            TokenTypes.ELLIPSIS,
199            TokenTypes.LABELED_STAT,
200            TokenTypes.METHOD_REF,
201        };
202    }
203
204    @Override
205    public int[] getRequiredTokens() {
206        return CommonUtil.EMPTY_INT_ARRAY;
207    }
208
209    @Override
210    public void visitToken(DetailAST ast) {
211        final String line = getLine(ast.getLineNo() - 1);
212        final int before = ast.getColumnNo() - 1;
213
214        if ((before == -1 || Character.isWhitespace(line.charAt(before)))
215                && !isInEmptyForInitializerOrCondition(ast)) {
216            boolean flag = !allowLineBreaks;
217            // verify all characters before '.' are whitespace
218            for (int i = 0; i <= before - 1; i++) {
219                if (!Character.isWhitespace(line.charAt(i))) {
220                    flag = true;
221                    break;
222                }
223            }
224            if (flag) {
225                log(ast, MSG_KEY, ast.getText());
226            }
227        }
228    }
229
230    /**
231     * Checks that semicolon is in empty for initializer or condition.
232     *
233     * @param semicolonAst DetailAST of semicolon.
234     * @return true if semicolon is in empty for initializer or condition.
235     */
236    private static boolean isInEmptyForInitializerOrCondition(DetailAST semicolonAst) {
237        boolean result = false;
238        final DetailAST sibling = semicolonAst.getPreviousSibling();
239        if (sibling != null
240                && (sibling.getType() == TokenTypes.FOR_INIT
241                        || sibling.getType() == TokenTypes.FOR_CONDITION)
242                && !sibling.hasChildren()) {
243            result = true;
244        }
245        return result;
246    }
247
248    /**
249     * Setter to control whether whitespace is allowed if the token is at a linebreak.
250     *
251     * @param allowLineBreaks whether whitespace should be
252     *     flagged at line breaks.
253     */
254    public void setAllowLineBreaks(boolean allowLineBreaks) {
255        this.allowLineBreaks = allowLineBreaks;
256    }
257
258}