1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.regexp;
21
22 import java.io.File;
23 import java.util.regex.Pattern;
24
25 import com.puppycrawl.tools.checkstyle.PropertyType;
26 import com.puppycrawl.tools.checkstyle.StatelessCheck;
27 import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
28 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
29 import com.puppycrawl.tools.checkstyle.api.FileText;
30
31 /**
32 * <div>
33 * Checks that a specified pattern matches across multiple lines in any file type.
34 * </div>
35 *
36 * <p>
37 * Rationale: This check can be used to when the regular expression can be span multiple lines.
38 * </p>
39 * <ul>
40 * <li>
41 * Property {@code fileExtensions} - Specify the file extensions of the files to process.
42 * Type is {@code java.lang.String[]}.
43 * Default value is {@code ""}.
44 * </li>
45 * <li>
46 * Property {@code format} - Specify the format of the regular expression to match.
47 * Type is {@code java.util.regex.Pattern}.
48 * Default value is {@code "$."}.
49 * </li>
50 * <li>
51 * Property {@code ignoreCase} - Control whether to ignore case when searching.
52 * Type is {@code boolean}.
53 * Default value is {@code false}.
54 * </li>
55 * <li>
56 * Property {@code matchAcrossLines} - Control whether to match expressions
57 * across multiple lines.
58 * Type is {@code boolean}.
59 * Default value is {@code false}.
60 * </li>
61 * <li>
62 * Property {@code maximum} - Specify the maximum number of matches required in each file.
63 * Type is {@code int}.
64 * Default value is {@code 0}.
65 * </li>
66 * <li>
67 * Property {@code message} - Specify the message which is used to notify about
68 * violations, if empty then default (hard-coded) message is used.
69 * Type is {@code java.lang.String}.
70 * Default value is {@code null}.
71 * </li>
72 * <li>
73 * Property {@code minimum} - Specify the minimum number of matches required in each file.
74 * Type is {@code int}.
75 * Default value is {@code 0}.
76 * </li>
77 * </ul>
78 *
79 * <p>
80 * Parent is {@code com.puppycrawl.tools.checkstyle.Checker}
81 * </p>
82 *
83 * <p>
84 * Violation Message Keys:
85 * </p>
86 * <ul>
87 * <li>
88 * {@code regexp.StackOverflowError}
89 * </li>
90 * <li>
91 * {@code regexp.empty}
92 * </li>
93 * <li>
94 * {@code regexp.exceeded}
95 * </li>
96 * <li>
97 * {@code regexp.minimum}
98 * </li>
99 * </ul>
100 *
101 * @since 5.0
102 */
103 @StatelessCheck
104 public 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 }