001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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 * <div> 034 * Checks line wrapping with separators. 035 * </div> 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 * 054 * <p> 055 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 056 * </p> 057 * 058 * <p> 059 * Violation Message Keys: 060 * </p> 061 * <ul> 062 * <li> 063 * {@code line.new} 064 * </li> 065 * <li> 066 * {@code line.previous} 067 * </li> 068 * </ul> 069 * 070 * @since 5.8 071 */ 072@StatelessCheck 073public class SeparatorWrapCheck 074 extends AbstractCheck { 075 076 /** 077 * A key is pointing to the warning message text in "messages.properties" 078 * file. 079 */ 080 public static final String MSG_LINE_PREVIOUS = "line.previous"; 081 082 /** 083 * A key is pointing to the warning message text in "messages.properties" 084 * file. 085 */ 086 public static final String MSG_LINE_NEW = "line.new"; 087 088 /** Specify policy on how to wrap lines. */ 089 private WrapOption option = WrapOption.EOL; 090 091 /** 092 * Setter to specify policy on how to wrap lines. 093 * 094 * @param optionStr string to decode option from 095 * @throws IllegalArgumentException if unable to decode 096 * @since 5.8 097 */ 098 public void setOption(String optionStr) { 099 option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); 100 } 101 102 @Override 103 public int[] getDefaultTokens() { 104 return new int[] { 105 TokenTypes.DOT, 106 TokenTypes.COMMA, 107 }; 108 } 109 110 @Override 111 public int[] getAcceptableTokens() { 112 return new int[] { 113 TokenTypes.DOT, 114 TokenTypes.COMMA, 115 TokenTypes.SEMI, 116 TokenTypes.ELLIPSIS, 117 TokenTypes.AT, 118 TokenTypes.LPAREN, 119 TokenTypes.RPAREN, 120 TokenTypes.ARRAY_DECLARATOR, 121 TokenTypes.RBRACK, 122 TokenTypes.METHOD_REF, 123 }; 124 } 125 126 @Override 127 public int[] getRequiredTokens() { 128 return CommonUtil.EMPTY_INT_ARRAY; 129 } 130 131 @Override 132 public void visitToken(DetailAST ast) { 133 final String text = ast.getText(); 134 final int colNo = ast.getColumnNo(); 135 final int lineNo = ast.getLineNo(); 136 final int[] currentLine = getLineCodePoints(lineNo - 1); 137 final boolean isLineEmptyAfterToken = CodePointUtil.isBlank( 138 Arrays.copyOfRange(currentLine, colNo + text.length(), currentLine.length) 139 ); 140 final boolean isLineEmptyBeforeToken = CodePointUtil.isBlank( 141 Arrays.copyOfRange(currentLine, 0, colNo) 142 ); 143 144 if (option == WrapOption.NL 145 && isLineEmptyAfterToken) { 146 log(ast, MSG_LINE_NEW, text); 147 } 148 else if (option == WrapOption.EOL 149 && isLineEmptyBeforeToken) { 150 log(ast, MSG_LINE_PREVIOUS, text); 151 } 152 } 153 154}