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.regexp; 021 022import java.io.File; 023import java.util.regex.Pattern; 024 025import com.puppycrawl.tools.checkstyle.PropertyType; 026import com.puppycrawl.tools.checkstyle.StatelessCheck; 027import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 028import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 029import com.puppycrawl.tools.checkstyle.api.FileText; 030 031/** 032 * <div> 033 * Checks that a specified pattern matches across multiple lines in any file type. 034 * </div> 035 * 036 * <p> 037 * Rationale: This check can be used to when the regular expression can be span multiple lines. 038 * </p> 039 * <ul> 040 * <li> 041 * Property {@code fileExtensions} - Specify the file extensions of the files to process. 042 * Type is {@code java.lang.String[]}. 043 * Default value is {@code ""}. 044 * </li> 045 * <li> 046 * Property {@code format} - Specify the format of the regular expression to match. 047 * Type is {@code java.util.regex.Pattern}. 048 * Default value is {@code "$."}. 049 * </li> 050 * <li> 051 * Property {@code ignoreCase} - Control whether to ignore case when searching. 052 * Type is {@code boolean}. 053 * Default value is {@code false}. 054 * </li> 055 * <li> 056 * Property {@code matchAcrossLines} - Control whether to match expressions 057 * across multiple lines. 058 * Type is {@code boolean}. 059 * Default value is {@code false}. 060 * </li> 061 * <li> 062 * Property {@code maximum} - Specify the maximum number of matches required in each file. 063 * Type is {@code int}. 064 * Default value is {@code 0}. 065 * </li> 066 * <li> 067 * Property {@code message} - Specify the message which is used to notify about 068 * violations, if empty then default (hard-coded) message is used. 069 * Type is {@code java.lang.String}. 070 * Default value is {@code null}. 071 * </li> 072 * <li> 073 * Property {@code minimum} - Specify the minimum number of matches required in each file. 074 * Type is {@code int}. 075 * Default value is {@code 0}. 076 * </li> 077 * </ul> 078 * 079 * <p> 080 * Parent is {@code com.puppycrawl.tools.checkstyle.Checker} 081 * </p> 082 * 083 * <p> 084 * Violation Message Keys: 085 * </p> 086 * <ul> 087 * <li> 088 * {@code regexp.StackOverflowError} 089 * </li> 090 * <li> 091 * {@code regexp.empty} 092 * </li> 093 * <li> 094 * {@code regexp.exceeded} 095 * </li> 096 * <li> 097 * {@code regexp.minimum} 098 * </li> 099 * </ul> 100 * 101 * @since 5.0 102 */ 103@StatelessCheck 104public class RegexpMultilineCheck extends AbstractFileSetCheck { 105 106 /** Specify the format of the regular expression to match. */ 107 @XdocsPropertyType(PropertyType.PATTERN) 108 private String format = "$."; 109 /** 110 * Specify the message which is used to notify about violations, 111 * if empty then default (hard-coded) message is used. 112 */ 113 private String message; 114 /** Specify the minimum number of matches required in each file. */ 115 private int minimum; 116 /** Specify the maximum number of matches required in each file. */ 117 private int maximum; 118 /** Control whether to ignore case when searching. */ 119 private boolean ignoreCase; 120 /** Control whether to match expressions across multiple lines. */ 121 private boolean matchAcrossLines; 122 123 /** The detector to use. */ 124 private MultilineDetector detector; 125 126 @Override 127 public void beginProcessing(String charset) { 128 final DetectorOptions options = DetectorOptions.newBuilder() 129 .reporter(this) 130 .compileFlags(getRegexCompileFlags()) 131 .format(format) 132 .message(message) 133 .minimum(minimum) 134 .maximum(maximum) 135 .ignoreCase(ignoreCase) 136 .build(); 137 detector = new MultilineDetector(options); 138 } 139 140 @Override 141 protected void processFiltered(File file, FileText fileText) { 142 detector.processLines(fileText); 143 } 144 145 /** 146 * Retrieves the compile-flags for the regular expression being built based 147 * on {@code matchAcrossLines}. 148 * 149 * @return The compile-flags. 150 */ 151 private int getRegexCompileFlags() { 152 final int result; 153 154 if (matchAcrossLines) { 155 result = Pattern.DOTALL; 156 } 157 else { 158 result = Pattern.MULTILINE; 159 } 160 161 return result; 162 } 163 164 /** 165 * Setter to specify the format of the regular expression to match. 166 * 167 * @param format the format of the regular expression to match. 168 * @since 5.0 169 */ 170 public void setFormat(String format) { 171 this.format = format; 172 } 173 174 /** 175 * Setter to specify the message which is used to notify about violations, 176 * if empty then default (hard-coded) message is used. 177 * 178 * @param message the message to report for a match. 179 * @since 5.0 180 */ 181 public void setMessage(String message) { 182 this.message = message; 183 } 184 185 /** 186 * Setter to specify the minimum number of matches required in each file. 187 * 188 * @param minimum the minimum number of matches required in each file. 189 * @since 5.0 190 */ 191 public void setMinimum(int minimum) { 192 this.minimum = minimum; 193 } 194 195 /** 196 * Setter to specify the maximum number of matches required in each file. 197 * 198 * @param maximum the maximum number of matches required in each file. 199 * @since 5.0 200 */ 201 public void setMaximum(int maximum) { 202 this.maximum = maximum; 203 } 204 205 /** 206 * Setter to control whether to ignore case when searching. 207 * 208 * @param ignoreCase whether to ignore case when searching. 209 * @since 5.0 210 */ 211 public void setIgnoreCase(boolean ignoreCase) { 212 this.ignoreCase = ignoreCase; 213 } 214 215 /** 216 * Setter to control whether to match expressions across multiple lines. 217 * 218 * @param matchAcrossLines whether to match expressions across multiple lines. 219 * @since 8.25 220 */ 221 public void setMatchAcrossLines(boolean matchAcrossLines) { 222 this.matchAcrossLines = matchAcrossLines; 223 } 224 225}