1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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 *
40 * @since 5.0
41 */
42 @StatelessCheck
43 public class RegexpMultilineCheck extends AbstractFileSetCheck {
44
45 /** Specify the format of the regular expression to match. */
46 @XdocsPropertyType(PropertyType.PATTERN)
47 private String format = "$.";
48 /**
49 * Specify the message which is used to notify about violations,
50 * if empty then default (hard-coded) message is used.
51 */
52 private String message;
53 /** Specify the minimum number of matches required in each file. */
54 private int minimum;
55 /** Specify the maximum number of matches required in each file. */
56 private int maximum;
57 /** Control whether to ignore case when searching. */
58 private boolean ignoreCase;
59 /** Control whether to match expressions across multiple lines. */
60 private boolean matchAcrossLines;
61
62 /** The detector to use. */
63 private MultilineDetector detector;
64
65 @Override
66 public void beginProcessing(String charset) {
67 final DetectorOptions options = DetectorOptions.newBuilder()
68 .reporter(this)
69 .compileFlags(getRegexCompileFlags())
70 .format(format)
71 .message(message)
72 .minimum(minimum)
73 .maximum(maximum)
74 .ignoreCase(ignoreCase)
75 .build();
76 detector = new MultilineDetector(options);
77 }
78
79 @Override
80 protected void processFiltered(File file, FileText fileText) {
81 detector.processLines(fileText);
82 }
83
84 /**
85 * Retrieves the compile-flags for the regular expression being built based
86 * on {@code matchAcrossLines}.
87 *
88 * @return The compile-flags.
89 */
90 private int getRegexCompileFlags() {
91 final int result;
92
93 if (matchAcrossLines) {
94 result = Pattern.DOTALL;
95 }
96 else {
97 result = Pattern.MULTILINE;
98 }
99
100 return result;
101 }
102
103 /**
104 * Setter to specify the format of the regular expression to match.
105 *
106 * @param format the format of the regular expression to match.
107 * @since 5.0
108 */
109 public void setFormat(String format) {
110 this.format = format;
111 }
112
113 /**
114 * Setter to specify the message which is used to notify about violations,
115 * if empty then default (hard-coded) message is used.
116 *
117 * @param message the message to report for a match.
118 * @since 5.0
119 */
120 public void setMessage(String message) {
121 this.message = message;
122 }
123
124 /**
125 * Setter to specify the minimum number of matches required in each file.
126 *
127 * @param minimum the minimum number of matches required in each file.
128 * @since 5.0
129 */
130 public void setMinimum(int minimum) {
131 this.minimum = minimum;
132 }
133
134 /**
135 * Setter to specify the maximum number of matches required in each file.
136 *
137 * @param maximum the maximum number of matches required in each file.
138 * @since 5.0
139 */
140 public void setMaximum(int maximum) {
141 this.maximum = maximum;
142 }
143
144 /**
145 * Setter to control whether to ignore case when searching.
146 *
147 * @param ignoreCase whether to ignore case when searching.
148 * @since 5.0
149 */
150 public void setIgnoreCase(boolean ignoreCase) {
151 this.ignoreCase = ignoreCase;
152 }
153
154 /**
155 * Setter to control whether to match expressions across multiple lines.
156 *
157 * @param matchAcrossLines whether to match expressions across multiple lines.
158 * @since 8.25
159 */
160 public void setMatchAcrossLines(boolean matchAcrossLines) {
161 this.matchAcrossLines = matchAcrossLines;
162 }
163
164 }