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 java.util.Locale; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 029 030/** 031 * <p> 032 * Checks line wrapping with separators. 033 * </p> 034 * <ul> 035 * <li> 036 * Property {@code option} - Specify policy on how to wrap lines. 037 * Default value is {@code eol}. 038 * </li> 039 * <li> 040 * Property {@code tokens} - tokens to check 041 * Default value is: 042 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT"> 043 * DOT</a>, 044 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMMA"> 045 * COMMA</a>. 046 * </li> 047 * </ul> 048 * <p> 049 * To configure the check: 050 * </p> 051 * <pre> 052 * <module name="SeparatorWrap"/> 053 * </pre> 054 * <p> 055 * Example: 056 * </p> 057 * <pre> 058 * import java.io. 059 * IOException; // OK 060 * 061 * class Test { 062 * 063 * String s; 064 * 065 * public void foo(int a, 066 * int b) { // OK 067 * } 068 * 069 * public void bar(int p 070 * , int q) { // violation, separator comma on new line 071 * if (s 072 * .isEmpty()) { // violation, separator dot on new line 073 * } 074 * } 075 * 076 * } 077 * </pre> 078 * <p> 079 * To configure the check for 080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_REF"> 081 * METHOD_REF</a> at new line: 082 * </p> 083 * <pre> 084 * <module name="SeparatorWrap"> 085 * <property name="tokens" value="METHOD_REF"/> 086 * <property name="option" value="nl"/> 087 * </module> 088 * </pre> 089 * <p> 090 * Example: 091 * </p> 092 * <pre> 093 * import java.util.Arrays; 094 * 095 * class Test2 { 096 * 097 * String[] stringArray = {"foo", "bar"}; 098 * 099 * void fun() { 100 * Arrays.sort(stringArray, String:: 101 * compareToIgnoreCase); // violation, separator method reference on same line 102 * Arrays.sort(stringArray, String 103 * ::compareTo); // OK 104 * } 105 * 106 * } 107 * </pre> 108 * <p> 109 * To configure the check for comma at the new line: 110 * </p> 111 * <pre> 112 * <module name="SeparatorWrap"> 113 * <property name="tokens" value="COMMA"/> 114 * <property name="option" value="nl"/> 115 * </module> 116 * </pre> 117 * <p> 118 * Example: 119 * </p> 120 * <pre> 121 * class Test3 { 122 * 123 * String s; 124 * 125 * int a, 126 * b; // violation, separator comma on same line 127 * 128 * public void foo(int a, 129 * int b) { // violation, separator comma on the same line 130 * int r 131 * , t; // OK 132 * } 133 * 134 * public void bar(int p 135 * , int q) { // OK 136 * } 137 * 138 * } 139 * </pre> 140 * 141 * @since 5.8 142 */ 143@StatelessCheck 144public class SeparatorWrapCheck 145 extends AbstractCheck { 146 147 /** 148 * A key is pointing to the warning message text in "messages.properties" 149 * file. 150 */ 151 public static final String MSG_LINE_PREVIOUS = "line.previous"; 152 153 /** 154 * A key is pointing to the warning message text in "messages.properties" 155 * file. 156 */ 157 public static final String MSG_LINE_NEW = "line.new"; 158 159 /** Specify policy on how to wrap lines. */ 160 private WrapOption option = WrapOption.EOL; 161 162 /** 163 * Setter to specify policy on how to wrap lines. 164 * 165 * @param optionStr string to decode option from 166 * @throws IllegalArgumentException if unable to decode 167 */ 168 public void setOption(String optionStr) { 169 option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); 170 } 171 172 @Override 173 public int[] getDefaultTokens() { 174 return new int[] { 175 TokenTypes.DOT, 176 TokenTypes.COMMA, 177 }; 178 } 179 180 @Override 181 public int[] getAcceptableTokens() { 182 return new int[] { 183 TokenTypes.DOT, 184 TokenTypes.COMMA, 185 TokenTypes.SEMI, 186 TokenTypes.ELLIPSIS, 187 TokenTypes.AT, 188 TokenTypes.LPAREN, 189 TokenTypes.RPAREN, 190 TokenTypes.ARRAY_DECLARATOR, 191 TokenTypes.RBRACK, 192 TokenTypes.METHOD_REF, 193 }; 194 } 195 196 @Override 197 public int[] getRequiredTokens() { 198 return CommonUtil.EMPTY_INT_ARRAY; 199 } 200 201 @Override 202 public void visitToken(DetailAST ast) { 203 final String text = ast.getText(); 204 final int colNo = ast.getColumnNo(); 205 final int lineNo = ast.getLineNo(); 206 final String currentLine = getLines()[lineNo - 1]; 207 final String substringAfterToken = 208 currentLine.substring(colNo + text.length()).trim(); 209 final String substringBeforeToken = 210 currentLine.substring(0, colNo).trim(); 211 212 if (option == WrapOption.EOL 213 && substringBeforeToken.isEmpty()) { 214 log(ast, MSG_LINE_PREVIOUS, text); 215 } 216 else if (option == WrapOption.NL 217 && substringAfterToken.isEmpty()) { 218 log(ast, MSG_LINE_NEW, text); 219 } 220 } 221 222}