View Javadoc
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 }