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 * Code example for comma and dot at the new line: 050 * </p> 051 * <pre> 052 * s 053 * .isEmpty(); 054 * foo(i 055 * ,s); 056 * </pre> 057 * <p> 058 * To configure the check: 059 * </p> 060 * <pre> 061 * <module name="SeparatorWrap"/> 062 * </pre> 063 * <p> 064 * Code example for comma and dot at the previous line: 065 * </p> 066 * <pre> 067 * s. 068 * isEmpty(); 069 * foo(i, 070 * s); 071 * </pre> 072 * <p> 073 * Example for checking method reference at new line (good case and bad case): 074 * </p> 075 * <pre> 076 * Arrays.sort(stringArray, String:: // violation 077 * compareToIgnoreCase); 078 * Arrays.sort(stringArray, String 079 * ::compareToIgnoreCase); // good 080 * </pre> 081 * <p> 082 * To configure the check for 083 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_REF"> 084 * METHOD_REF</a> at new line: 085 * </p> 086 * <pre> 087 * <module name="SeparatorWrap"> 088 * <property name="tokens" value="METHOD_REF"/> 089 * <property name="option" value="nl"/> 090 * </module> 091 * </pre> 092 * <p> 093 * To configure the check for comma at the new line: 094 * </p> 095 * <pre> 096 * <module name="SeparatorWrap"> 097 * <property name="tokens" value="COMMA"/> 098 * <property name="option" value="nl"/> 099 * </module> 100 * </pre> 101 * 102 * @since 5.8 103 */ 104@StatelessCheck 105public class SeparatorWrapCheck 106 extends AbstractCheck { 107 108 /** 109 * A key is pointing to the warning message text in "messages.properties" 110 * file. 111 */ 112 public static final String MSG_LINE_PREVIOUS = "line.previous"; 113 114 /** 115 * A key is pointing to the warning message text in "messages.properties" 116 * file. 117 */ 118 public static final String MSG_LINE_NEW = "line.new"; 119 120 /** Specify policy on how to wrap lines. */ 121 private WrapOption option = WrapOption.EOL; 122 123 /** 124 * Setter to specify policy on how to wrap lines. 125 * @param optionStr string to decode option from 126 * @throws IllegalArgumentException if unable to decode 127 */ 128 public void setOption(String optionStr) { 129 option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); 130 } 131 132 @Override 133 public int[] getDefaultTokens() { 134 return new int[] { 135 TokenTypes.DOT, 136 TokenTypes.COMMA, 137 }; 138 } 139 140 @Override 141 public int[] getAcceptableTokens() { 142 return new int[] { 143 TokenTypes.DOT, 144 TokenTypes.COMMA, 145 TokenTypes.SEMI, 146 TokenTypes.ELLIPSIS, 147 TokenTypes.AT, 148 TokenTypes.LPAREN, 149 TokenTypes.RPAREN, 150 TokenTypes.ARRAY_DECLARATOR, 151 TokenTypes.RBRACK, 152 TokenTypes.METHOD_REF, 153 }; 154 } 155 156 @Override 157 public int[] getRequiredTokens() { 158 return CommonUtil.EMPTY_INT_ARRAY; 159 } 160 161 @Override 162 public void visitToken(DetailAST ast) { 163 final String text = ast.getText(); 164 final int colNo = ast.getColumnNo(); 165 final int lineNo = ast.getLineNo(); 166 final String currentLine = getLines()[lineNo - 1]; 167 final String substringAfterToken = 168 currentLine.substring(colNo + text.length()).trim(); 169 final String substringBeforeToken = 170 currentLine.substring(0, colNo).trim(); 171 172 if (option == WrapOption.EOL 173 && substringBeforeToken.isEmpty()) { 174 log(ast, MSG_LINE_PREVIOUS, text); 175 } 176 else if (option == WrapOption.NL 177 && substringAfterToken.isEmpty()) { 178 log(ast, MSG_LINE_NEW, text); 179 } 180 } 181 182}