001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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 the policy on how to wrap lines on operators. 033 * </p> 034 * <ul> 035 * <li> 036 * Property {@code option} - Specify policy on how to wrap lines. 037 * Default value is {@code nl}. 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#QUESTION"> 043 * QUESTION</a>, 044 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COLON"> 045 * COLON</a>, 046 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL"> 047 * EQUAL</a>, 048 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL"> 049 * NOT_EQUAL</a>, 050 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV"> 051 * DIV</a>, 052 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS"> 053 * PLUS</a>, 054 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS"> 055 * MINUS</a>, 056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR"> 057 * STAR</a>, 058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD"> 059 * MOD</a>, 060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR"> 061 * SR</a>, 062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR"> 063 * BSR</a>, 064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE"> 065 * GE</a>, 066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT"> 067 * GT</a>, 068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL"> 069 * SL</a>, 070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE"> 071 * LE</a>, 072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT"> 073 * LT</a>, 074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR"> 075 * BXOR</a>, 076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR"> 077 * BOR</a>, 078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LOR"> 079 * LOR</a>, 080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND"> 081 * BAND</a>, 082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND"> 083 * LAND</a>, 084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPE_EXTENSION_AND"> 085 * TYPE_EXTENSION_AND</a>, 086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_INSTANCEOF"> 087 * LITERAL_INSTANCEOF</a>. 088 * </li> 089 * </ul> 090 * <p> 091 * To configure the check: 092 * </p> 093 * <pre> 094 * <module name="OperatorWrap"/> 095 * </pre> 096 * <p> 097 * To configure the check for assignment operators at the end of a line: 098 * </p> 099 * <pre> 100 * <module name="OperatorWrap"> 101 * <property name="tokens" 102 * value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN, 103 * SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/> 104 * <property name="option" value="eol"/> 105 * </module> 106 * </pre> 107 * 108 * @since 3.0 109 */ 110@StatelessCheck 111public class OperatorWrapCheck 112 extends AbstractCheck { 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 /** 121 * A key is pointing to the warning message text in "messages.properties" 122 * file. 123 */ 124 public static final String MSG_LINE_PREVIOUS = "line.previous"; 125 126 /** Specify policy on how to wrap lines. */ 127 private WrapOption option = WrapOption.NL; 128 129 /** 130 * Setter to specify policy on how to wrap lines. 131 * @param optionStr string to decode option from 132 * @throws IllegalArgumentException if unable to decode 133 */ 134 public void setOption(String optionStr) { 135 option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); 136 } 137 138 @Override 139 public int[] getDefaultTokens() { 140 return new int[] { 141 TokenTypes.QUESTION, // '?' 142 TokenTypes.COLON, // ':' (not reported for a case) 143 TokenTypes.EQUAL, // "==" 144 TokenTypes.NOT_EQUAL, // "!=" 145 TokenTypes.DIV, // '/' 146 TokenTypes.PLUS, //' +' (unary plus is UNARY_PLUS) 147 TokenTypes.MINUS, // '-' (unary minus is UNARY_MINUS) 148 TokenTypes.STAR, // '*' 149 TokenTypes.MOD, // '%' 150 TokenTypes.SR, // ">>" 151 TokenTypes.BSR, // ">>>" 152 TokenTypes.GE, // ">=" 153 TokenTypes.GT, // ">" 154 TokenTypes.SL, // "<<" 155 TokenTypes.LE, // "<=" 156 TokenTypes.LT, // '<' 157 TokenTypes.BXOR, // '^' 158 TokenTypes.BOR, // '|' 159 TokenTypes.LOR, // "||" 160 TokenTypes.BAND, // '&' 161 TokenTypes.LAND, // "&&" 162 TokenTypes.TYPE_EXTENSION_AND, 163 TokenTypes.LITERAL_INSTANCEOF, 164 }; 165 } 166 167 @Override 168 public int[] getAcceptableTokens() { 169 return new int[] { 170 TokenTypes.QUESTION, // '?' 171 TokenTypes.COLON, // ':' (not reported for a case) 172 TokenTypes.EQUAL, // "==" 173 TokenTypes.NOT_EQUAL, // "!=" 174 TokenTypes.DIV, // '/' 175 TokenTypes.PLUS, //' +' (unary plus is UNARY_PLUS) 176 TokenTypes.MINUS, // '-' (unary minus is UNARY_MINUS) 177 TokenTypes.STAR, // '*' 178 TokenTypes.MOD, // '%' 179 TokenTypes.SR, // ">>" 180 TokenTypes.BSR, // ">>>" 181 TokenTypes.GE, // ">=" 182 TokenTypes.GT, // ">" 183 TokenTypes.SL, // "<<" 184 TokenTypes.LE, // "<=" 185 TokenTypes.LT, // '<' 186 TokenTypes.BXOR, // '^' 187 TokenTypes.BOR, // '|' 188 TokenTypes.LOR, // "||" 189 TokenTypes.BAND, // '&' 190 TokenTypes.LAND, // "&&" 191 TokenTypes.LITERAL_INSTANCEOF, 192 TokenTypes.TYPE_EXTENSION_AND, 193 TokenTypes.ASSIGN, // '=' 194 TokenTypes.DIV_ASSIGN, // "/=" 195 TokenTypes.PLUS_ASSIGN, // "+=" 196 TokenTypes.MINUS_ASSIGN, //"-=" 197 TokenTypes.STAR_ASSIGN, // "*=" 198 TokenTypes.MOD_ASSIGN, // "%=" 199 TokenTypes.SR_ASSIGN, // ">>=" 200 TokenTypes.BSR_ASSIGN, // ">>>=" 201 TokenTypes.SL_ASSIGN, // "<<=" 202 TokenTypes.BXOR_ASSIGN, // "^=" 203 TokenTypes.BOR_ASSIGN, // "|=" 204 TokenTypes.BAND_ASSIGN, // "&=" 205 TokenTypes.METHOD_REF, // "::" 206 }; 207 } 208 209 @Override 210 public int[] getRequiredTokens() { 211 return CommonUtil.EMPTY_INT_ARRAY; 212 } 213 214 @Override 215 public void visitToken(DetailAST ast) { 216 final DetailAST parent = ast.getParent(); 217 //we do not want to check colon for cases and defaults 218 if (parent.getType() != TokenTypes.LITERAL_DEFAULT 219 && parent.getType() != TokenTypes.LITERAL_CASE) { 220 final String text = ast.getText(); 221 final int colNo = ast.getColumnNo(); 222 final int lineNo = ast.getLineNo(); 223 final String currentLine = getLine(lineNo - 1); 224 225 // Check if rest of line is whitespace, and not just the operator 226 // by itself. This last bit is to handle the operator on a line by 227 // itself. 228 if (option == WrapOption.NL 229 && !text.equals(currentLine.trim()) 230 && CommonUtil.isBlank(currentLine.substring(colNo + text.length()))) { 231 log(ast, MSG_LINE_NEW, text); 232 } 233 else if (option == WrapOption.EOL 234 && CommonUtil.hasWhitespaceBefore(colNo - 1, currentLine)) { 235 log(ast, MSG_LINE_PREVIOUS, text); 236 } 237 } 238 } 239 240}