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