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.regexp; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 026 027/** 028 * <p> 029 * Checks that a specified pattern matches a single line in Java files. 030 * </p> 031 * <p> 032 * This class is variation on 033 * <a href="https://checkstyle.org/config_regexp.html#RegexpSingleline">RegexpSingleline</a> 034 * for detecting single lines that match a supplied regular expression in Java files. 035 * It supports suppressing matches in Java comments. 036 * </p> 037 * <ul> 038 * <li> 039 * Property {@code format} - Specify the format of the regular expression to match. 040 * Default value is {@code "$."}. 041 * </li> 042 * <li> 043 * Property {@code message} - Specify the message which is used to notify about 044 * violations, if empty then default (hard-coded) message is used. 045 * Default value is {@code null}. 046 * </li> 047 * <li> 048 * Property {@code ignoreCase} - Control whether to ignore case when searching. 049 * Default value is {@code false}. 050 * </li> 051 * <li> 052 * Property {@code minimum} - Specify the minimum number of matches required in each file. 053 * Default value is {@code 0}. 054 * </li> 055 * <li> 056 * Property {@code maximum} - Specify the maximum number of matches required in each file. 057 * Default value is {@code 0}. 058 * </li> 059 * <li> 060 * Property {@code ignoreComments} - Control whether to ignore text in comments when searching. 061 * Default value is {@code false}. 062 * </li> 063 * </ul> 064 * <p> 065 * To configure the check with default values: 066 * </p> 067 * <pre> 068 * <module name="RegexpSinglelineJava"/> 069 * </pre> 070 * <p> 071 * This configuration does not match to anything, 072 * so we do not provide any code example for it 073 * as no violation will ever be reported. 074 * </p> 075 * <p> 076 * To configure the check for calls to {@code System.out.println}, except in comments: 077 * </p> 078 * <pre> 079 * <module name="RegexpSinglelineJava"> 080 * <!-- . matches any character, so we need to 081 * escape it and use \. to match dots. --> 082 * <property name="format" value="System\.out\.println"/> 083 * <property name="ignoreComments" value="true"/> 084 * </module> 085 * </pre> 086 * <p>Example:</p> 087 * <pre> 088 * System.out.println(""); // violation, instruction matches illegal pattern 089 * System.out. 090 * println(""); // OK 091 * /* System.out.println */ // OK, comments are ignored 092 * </pre> 093 * <p> 094 * To configure the check to find case-insensitive occurrences of "debug": 095 * </p> 096 * <pre> 097 * <module name="RegexpSinglelineJava"> 098 * <property name="format" value="debug"/> 099 * <property name="ignoreCase" value="true"/> 100 * </module> 101 * </pre> 102 * <p>Example:</p> 103 * <pre> 104 * int debug = 0; // violation, variable name matches illegal pattern 105 * public class Debug { // violation, class name matches illegal pattern 106 * /* this is for de 107 * bug only; */ // OK 108 * </pre> 109 * <p> 110 * To configure the check to find occurrences of 111 * "\.read(.*)|\.write(.*)" 112 * and display "IO found" for each violation. 113 * </p> 114 * <pre> 115 * <module name="RegexpSinglelineJava"> 116 * <property name="format" value="\.read(.*)|\.write(.*)"/> 117 * <property name="message" value="IO found"/> 118 * </module> 119 * </pre> 120 * <p>Example:</p> 121 * <pre> 122 * FileReader in = new FileReader("path/to/input"); 123 * int ch = in.read(); // violation 124 * while(ch != -1) { 125 * System.out.print((char)ch); 126 * ch = in.read(); // violation 127 * } 128 * 129 * FileWriter out = new FileWriter("path/to/output"); 130 * out.write("something"); // violation 131 * </pre> 132 * <p> 133 * To configure the check to find occurrences of 134 * "\.log(.*)". We want to allow a maximum of 2 occurrences. 135 * </p> 136 * <pre> 137 * <module name="RegexpSinglelineJava"> 138 * <property name="format" value="\.log(.*)"/> 139 * <property name="maximum" value="2"/> 140 * </module> 141 * </pre> 142 * <p>Example:</p> 143 * <pre> 144 * public class Foo{ 145 * public void bar(){ 146 * Logger.log("first"); // OK, first occurrence is allowed 147 * Logger.log("second"); // OK, second occurrence is allowed 148 * Logger.log("third"); // violation 149 * System.out.println("fourth"); 150 * Logger.log("fifth"); // violation 151 * } 152 * } 153 * </pre> 154 * <p> 155 * To configure the check to find all occurrences of 156 * "public". We want to ignore comments, 157 * display "public member found" for each violation 158 * and say if less than 2 occurrences. 159 * </p> 160 * <pre> 161 * <module name="RegexpSinglelineJava"> 162 * <property name="format" value="public"/> 163 * <property name="minimum" value="2"/> 164 * <property name="message" value="public member found"/> 165 * <property name="ignoreComments" value="true"/> 166 * </module> 167 * </pre> 168 * <p>Example:</p> 169 * <pre> 170 * class Foo{ // violation, file contains less than 2 occurrences of "public" 171 * private int a; 172 * /* public comment */ // OK, comment is ignored 173 * private void bar1() {} 174 * public void bar2() {} // violation 175 * } 176 * </pre> 177 * <p>Example:</p> 178 * <pre> 179 * class Foo{ 180 * private int a; 181 * /* public comment */ // OK, comment is ignored 182 * public void bar1() {} // violation 183 * public void bar2() {} // violation 184 * } 185 * </pre> 186 * 187 * @since 6.0 188 */ 189@StatelessCheck 190public class RegexpSinglelineJavaCheck extends AbstractCheck { 191 192 /** Specify the format of the regular expression to match. */ 193 private String format = "$."; 194 /** 195 * Specify the message which is used to notify about violations, 196 * if empty then default (hard-coded) message is used. 197 */ 198 private String message; 199 /** Specify the minimum number of matches required in each file. */ 200 private int minimum; 201 /** Specify the maximum number of matches required in each file. */ 202 private int maximum; 203 /** Control whether to ignore case when searching. */ 204 private boolean ignoreCase; 205 /** Control whether to ignore text in comments when searching. */ 206 private boolean ignoreComments; 207 208 @Override 209 public int[] getDefaultTokens() { 210 return getRequiredTokens(); 211 } 212 213 @Override 214 public int[] getAcceptableTokens() { 215 return getRequiredTokens(); 216 } 217 218 @Override 219 public int[] getRequiredTokens() { 220 return CommonUtil.EMPTY_INT_ARRAY; 221 } 222 223 @Override 224 public void beginTree(DetailAST rootAST) { 225 MatchSuppressor suppressor = null; 226 if (ignoreComments) { 227 suppressor = new CommentSuppressor(getFileContents()); 228 } 229 230 final DetectorOptions options = DetectorOptions.newBuilder() 231 .reporter(this) 232 .compileFlags(0) 233 .suppressor(suppressor) 234 .format(format) 235 .message(message) 236 .minimum(minimum) 237 .maximum(maximum) 238 .ignoreCase(ignoreCase) 239 .build(); 240 final SinglelineDetector detector = new SinglelineDetector(options); 241 detector.processLines(getFileContents().getText()); 242 } 243 244 /** 245 * Setter to specify the format of the regular expression to match. 246 * 247 * @param format the format of the regular expression to match. 248 */ 249 public void setFormat(String format) { 250 this.format = format; 251 } 252 253 /** 254 * Setter to specify the message which is used to notify about violations, 255 * if empty then default (hard-coded) message is used. 256 * 257 * @param message the message to report for a match. 258 */ 259 public void setMessage(String message) { 260 this.message = message; 261 } 262 263 /** 264 * Setter to specify the minimum number of matches required in each file. 265 * 266 * @param minimum the minimum number of matches required in each file. 267 */ 268 public void setMinimum(int minimum) { 269 this.minimum = minimum; 270 } 271 272 /** 273 * Setter to specify the maximum number of matches required in each file. 274 * 275 * @param maximum the maximum number of matches required in each file. 276 */ 277 public void setMaximum(int maximum) { 278 this.maximum = maximum; 279 } 280 281 /** 282 * Setter to control whether to ignore case when searching. 283 * 284 * @param ignoreCase whether to ignore case when searching. 285 */ 286 public void setIgnoreCase(boolean ignoreCase) { 287 this.ignoreCase = ignoreCase; 288 } 289 290 /** 291 * Setter to control whether to ignore text in comments when searching. 292 * 293 * @param ignore whether to ignore text in comments when searching. 294 */ 295 public void setIgnoreComments(boolean ignore) { 296 ignoreComments = ignore; 297 } 298 299}