| //////////////////////////////////////////////////////////////////////////////// |
| // checkstyle: Checks Java source code for adherence to a set of rules. |
| // Copyright (C) 2001-2017 the original author or authors. |
| // |
| // This library is free software; you can redistribute it and/or |
| // modify it under the terms of the GNU Lesser General Public |
| // License as published by the Free Software Foundation; either |
| // version 2.1 of the License, or (at your option) any later version. |
| // |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| // Lesser General Public License for more details. |
| // |
| // You should have received a copy of the GNU Lesser General Public |
| // License along with this library; if not, write to the Free Software |
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package com.puppycrawl.tools.checkstyle.checks.whitespace; |
| |
| import com.puppycrawl.tools.checkstyle.StatelessCheck; |
| import com.puppycrawl.tools.checkstyle.api.AbstractCheck; |
| import com.puppycrawl.tools.checkstyle.api.DetailAST; |
| import com.puppycrawl.tools.checkstyle.utils.CommonUtils; |
| |
| /** |
| * <p> |
| * Checks that non-whitespace characters are separated by no more than one |
| * whitespace. Separating characters by tabs or multiple spaces will be |
| * reported. Currently the check doesn't permit horizontal alignment. To inspect |
| * whitespaces before and after comments, set the property |
| * <b>validateComments</b> to true. |
| * </p> |
| * |
| * <p> |
| * Setting <b>validateComments</b> to false will ignore cases like: |
| * </p> |
| * |
| * <pre> |
| * int i; // Multiple whitespaces before comment tokens will be ignored. |
| * private void foo(int /* whitespaces before and after block-comments will be |
| * ignored */ i) { |
| * </pre> |
| * |
| * <p> |
| * Sometimes, users like to space similar items on different lines to the same |
| * column position for easier reading. This feature isn't supported by this |
| * check, so both braces in the following case will be reported as violations. |
| * </p> |
| * |
| * <pre> |
| * public long toNanos(long d) { return d; } // 2 violations |
| * public long toMicros(long d) { return d / (C1 / C0); } |
| * </pre> |
| * |
| * <p> |
| * Check have following options: |
| * </p> |
| * |
| * <ul> |
| * <li>validateComments - Boolean when set to {@code true}, whitespaces |
| * surrounding comments will be ignored. Default value is {@code false}.</li> |
| * </ul> |
| * |
| * <p> |
| * To configure the check: |
| * </p> |
| * |
| * <pre> |
| * <module name="SingleSpaceSeparator"/> |
| * </pre> |
| * |
| * <p> |
| * To configure the check so that it validates comments: |
| * </p> |
| * |
| * <pre> |
| * <module name="SingleSpaceSeparator"> |
| * <property name="validateComments" value="true"/> |
| * </module> |
| * </pre> |
| * |
| * @author Robert Whitebit |
| * @author Richard Veach |
| */ |
| @StatelessCheck |
| public class SingleSpaceSeparatorCheck extends AbstractCheck { |
| /** |
| * A key is pointing to the warning message text in "messages.properties" |
| * file. |
| */ |
| public static final String MSG_KEY = "single.space.separator"; |
| |
| /** Indicates if whitespaces surrounding comments will be ignored. */ |
| private boolean validateComments; |
| |
| /** |
| * Sets whether or not to validate surrounding whitespaces at comments. |
| * |
| * @param validateComments {@code true} to validate surrounding whitespaces at comments. |
| */ |
| public void setValidateComments(boolean validateComments) { |
| this.validateComments = validateComments; |
| } |
| |
| @Override |
| public int[] getDefaultTokens() { |
| return getRequiredTokens(); |
| } |
| |
| @Override |
| public int[] getAcceptableTokens() { |
| return getRequiredTokens(); |
| } |
| |
| @Override |
| public int[] getRequiredTokens() { |
| return CommonUtils.EMPTY_INT_ARRAY; |
| } |
| |
| // -@cs[SimpleAccessorNameNotation] Overrides method from base class. |
| // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166 |
| @Override |
| public boolean isCommentNodesRequired() { |
| return validateComments; |
| } |
| |
| @Override |
| public void beginTree(DetailAST rootAST) { |
| visitEachToken(rootAST); |
| } |
| |
| /** |
| * Examines every sibling and child of {@code node} for violations. |
| * |
| * @param node The node to start examining. |
| */ |
| private void visitEachToken(DetailAST node) { |
| DetailAST sibling = node; |
| |
| while (sibling != null) { |
| final int columnNo = sibling.getColumnNo() - 1; |
| |
| // in such expression: "j =123", placed at the start of the string index of the second |
| // space character will be: 2 = 0(j) + 1(whitespace) + 1(whitespace). It is a minimal |
| // possible index for the second whitespace between non-whitespace characters. |
| final int minSecondWhitespaceColumnNo = 2; |
| |
| if (columnNo >= minSecondWhitespaceColumnNo |
| && !isTextSeparatedCorrectlyFromPrevious(getLine(sibling.getLineNo() - 1), |
| columnNo)) { |
| log(sibling.getLineNo(), columnNo, MSG_KEY); |
| } |
| if (sibling.getChildCount() >= 1) { |
| visitEachToken(sibling.getFirstChild()); |
| } |
| |
| sibling = sibling.getNextSibling(); |
| } |
| } |
| |
| /** |
| * Checks if characters in {@code line} at and around {@code columnNo} has |
| * the correct number of spaces. to return {@code true} the following |
| * conditions must be met:<br /> |
| * - the character at {@code columnNo} is the first in the line.<br /> |
| * - the character at {@code columnNo} is not separated by whitespaces from |
| * the previous non-whitespace character. <br /> |
| * - the character at {@code columnNo} is separated by only one whitespace |
| * from the previous non-whitespace character.<br /> |
| * - {@link #validateComments} is disabled and the previous text is the |
| * end of a block comment. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if the text at {@code columnNo} is separated |
| * correctly from the previous token. |
| */ |
| private boolean isTextSeparatedCorrectlyFromPrevious(String line, int columnNo) { |
| return isSingleSpace(line, columnNo) |
| || !isWhitespace(line, columnNo) |
| || isFirstInLine(line, columnNo) |
| || !validateComments && isBlockCommentEnd(line, columnNo); |
| } |
| |
| /** |
| * Checks if the {@code line} at {@code columnNo} is a single space, and not |
| * preceded by another space. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if the character at {@code columnNo} is a space, and |
| * not preceded by another space. |
| */ |
| private static boolean isSingleSpace(String line, int columnNo) { |
| return !isPrecededByMultipleWhitespaces(line, columnNo) |
| && isSpace(line, columnNo); |
| } |
| |
| /** |
| * Checks if the {@code line} at {@code columnNo} is a space. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if the character at {@code columnNo} is a space. |
| */ |
| private static boolean isSpace(String line, int columnNo) { |
| return line.charAt(columnNo) == ' '; |
| } |
| |
| /** |
| * Checks if the {@code line} at {@code columnNo} is preceded by at least 2 |
| * whitespaces. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if there are at least 2 whitespace characters before |
| * {@code columnNo}. |
| */ |
| private static boolean isPrecededByMultipleWhitespaces(String line, int columnNo) { |
| return Character.isWhitespace(line.charAt(columnNo)) |
| && Character.isWhitespace(line.charAt(columnNo - 1)); |
| } |
| |
| /** |
| * Checks if the {@code line} at {@code columnNo} is a whitespace character. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if the character at {@code columnNo} is a |
| * whitespace. |
| */ |
| private static boolean isWhitespace(String line, int columnNo) { |
| return Character.isWhitespace(line.charAt(columnNo)); |
| } |
| |
| /** |
| * Checks if the {@code line} up to and including {@code columnNo} is all |
| * non-whitespace text encountered. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if the column position is the first non-whitespace |
| * text on the {@code line}. |
| */ |
| private static boolean isFirstInLine(String line, int columnNo) { |
| return CommonUtils.isBlank(line.substring(0, columnNo)); |
| } |
| |
| /** |
| * Checks if the {@code line} at {@code columnNo} is the end of a comment, |
| * '*/'. |
| * |
| * @param line The line in the file to examine. |
| * @param columnNo The column position in the {@code line} to examine. |
| * @return {@code true} if the previous text is a end comment block. |
| */ |
| private static boolean isBlockCommentEnd(String line, int columnNo) { |
| return line.substring(0, columnNo).trim().endsWith("*/"); |
| } |
| } |