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