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 java.io.File; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 026import com.puppycrawl.tools.checkstyle.api.FileText; 027 028/** 029 * <p> 030 * Checks that a specified pattern matches a single line in any file type. 031 * </p> 032 * <p> 033 * Rationale: This check can be used to prototype checks and to find common bad 034 * practice such as calling {@code ex.printStacktrace()}, 035 * {@code System.out.println()}, {@code System.exit()}, etc. 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 fileExtensions} - Specify the file type extension of files to process. 061 * Default value is {@code all files}. 062 * </li> 063 * </ul> 064 * <p> 065 * To configure the check with default values: 066 * </p> 067 * <pre> 068 * <module name="RegexpSingleline" /> 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 to find occurrences of 'System.exit(' 077 * with some <i>slack</i> of allowing only one occurrence per file: 078 * </p> 079 * <pre> 080 * <module name="RegexpSingleline"> 081 * <property name="format" value="System.exit\("/> 082 * <!-- next line not required as 0 is the default --> 083 * <property name="minimum" value="0"/> 084 * <property name="maximum" value="1"/> 085 * </module> 086 * </pre> 087 * <p>Example:</p> 088 * <pre> 089 * class MyClass { 090 * void myFunction() { 091 * try { 092 * doSomething(); 093 * } catch (Exception e) { 094 * System.exit(1); // OK, as only there is only one occurrence. 095 * } 096 * } 097 * void doSomething(){}; 098 * } 099 * </pre> 100 * <pre> 101 * class MyClass { 102 * void myFunction() { 103 * try { 104 * doSomething(); 105 * System.exit(0); 106 * } catch (Exception e) { 107 * System.exit(1); // Violation, as there are more than one occurrence. 108 * } 109 * } 110 * void doSomething(){}; 111 * } 112 * </pre> 113 * <p> 114 * An example of how to configure the check to make sure a copyright statement 115 * is included in the file: 116 * </p> 117 * <pre> 118 * <module name="RegexpSingleline"> 119 * <property name="format" value="This file is copyrighted"/> 120 * <property name="minimum" value="1"/> 121 * <!-- Need to specify a maximum, so 10 times is more than enough. --> 122 * <property name="maximum" value="10"/> 123 * </module> 124 * </pre> 125 * <p>Example:</p> 126 * <pre> 127 * /** 128 * * This file is copyrighted under CC. // Ok, as the file contains a copyright statement. 129 * */ 130 * class MyClass { 131 * 132 * } 133 * </pre> 134 * <pre> 135 * /** // violation, as the file doesn't contain a copyright statement. 136 * * MyClass as a configuration example. 137 * */ 138 * class MyClass { 139 * 140 * } 141 * </pre> 142 * <p> 143 * An example of how to configure the check to make sure sql files contains the term 'license'. 144 * </p> 145 * <pre> 146 * <module name="RegexpSingleline"> 147 * <property name="format" value="license"/> 148 * <property name="minimum" value="1"/> 149 * <property name="maximum" value="9999"/> 150 * <property name="ignoreCase" value="true"/> 151 * <!-- Configure a message to be shown on violation of the Check. --> 152 * <property name="message" 153 * value="File must contain at least one occurrence of 'license' term"/> 154* <!-- Perform the Check only on files with java extension. --> 155 * <property name="fileExtensions" value="sql"/> 156 * </module> 157 * </pre> 158 * <p>Example:</p> 159 * <pre> 160 * /* 161 * AP 2.0 License. // Ok, Check ignores the case of the term. 162 * */ 163 * CREATE DATABASE MyDB; 164 * </pre> 165 * <pre> 166 * /* // violation, file doesn't contain the term. 167 * Example sql file. 168 * */ 169 * CREATE DATABASE MyDB; 170 * </pre> 171 * 172 * @since 5.0 173 */ 174@StatelessCheck 175public class RegexpSinglelineCheck extends AbstractFileSetCheck { 176 177 /** Specify the format of the regular expression to match. */ 178 private String format = "$."; 179 /** 180 * Specify the message which is used to notify about violations, 181 * if empty then default (hard-coded) message is used. 182 */ 183 private String message; 184 /** Specify the minimum number of matches required in each file. */ 185 private int minimum; 186 /** Specify the maximum number of matches required in each file. */ 187 private int maximum; 188 /** Control whether to ignore case when searching. */ 189 private boolean ignoreCase; 190 191 /** The detector to use. */ 192 private SinglelineDetector detector; 193 194 @Override 195 public void beginProcessing(String charset) { 196 final DetectorOptions options = DetectorOptions.newBuilder() 197 .reporter(this) 198 .compileFlags(0) 199 .format(format) 200 .message(message) 201 .minimum(minimum) 202 .maximum(maximum) 203 .ignoreCase(ignoreCase) 204 .build(); 205 detector = new SinglelineDetector(options); 206 } 207 208 @Override 209 protected void processFiltered(File file, FileText fileText) { 210 detector.processLines(fileText); 211 } 212 213 /** 214 * Setter to specify the format of the regular expression to match. 215 * 216 * @param format the format of the regular expression to match. 217 */ 218 public void setFormat(String format) { 219 this.format = format; 220 } 221 222 /** 223 * Setter to specify the message which is used to notify about violations, 224 * if empty then default (hard-coded) message is used. 225 * 226 * @param message the message to report for a match. 227 */ 228 public void setMessage(String message) { 229 this.message = message; 230 } 231 232 /** 233 * Setter to specify the minimum number of matches required in each file. 234 * 235 * @param minimum the minimum number of matches required in each file. 236 */ 237 public void setMinimum(int minimum) { 238 this.minimum = minimum; 239 } 240 241 /** 242 * Setter to specify the maximum number of matches required in each file. 243 * 244 * @param maximum the maximum number of matches required in each file. 245 */ 246 public void setMaximum(int maximum) { 247 this.maximum = maximum; 248 } 249 250 /** 251 * Setter to control whether to ignore case when searching. 252 * 253 * @param ignoreCase whether to ignore case when searching. 254 */ 255 public void setIgnoreCase(boolean ignoreCase) { 256 this.ignoreCase = ignoreCase; 257 } 258 259}