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   * <p>
33   * Checks that a specified pattern matches across multiple lines in any file type.
34   * </p>
35   * <p>
36   * Rationale: This check can be used to when the regular expression can be span multiple lines.
37   * </p>
38   * <ul>
39   * <li>
40   * Property {@code fileExtensions} - Specify the file extensions of the files to process.
41   * Type is {@code java.lang.String[]}.
42   * Default value is {@code ""}.
43   * </li>
44   * <li>
45   * Property {@code format} - Specify the format of the regular expression to match.
46   * Type is {@code java.util.regex.Pattern}.
47   * Default value is {@code "$."}.
48   * </li>
49   * <li>
50   * Property {@code ignoreCase} - Control whether to ignore case when searching.
51   * Type is {@code boolean}.
52   * Default value is {@code false}.
53   * </li>
54   * <li>
55   * Property {@code matchAcrossLines} - Control whether to match expressions
56   * across multiple lines.
57   * Type is {@code boolean}.
58   * Default value is {@code false}.
59   * </li>
60   * <li>
61   * Property {@code maximum} - Specify the maximum number of matches required in each file.
62   * Type is {@code int}.
63   * Default value is {@code 0}.
64   * </li>
65   * <li>
66   * Property {@code message} - Specify the message which is used to notify about
67   * violations, if empty then default (hard-coded) message is used.
68   * Type is {@code java.lang.String}.
69   * Default value is {@code null}.
70   * </li>
71   * <li>
72   * Property {@code minimum} - Specify the minimum number of matches required in each file.
73   * Type is {@code int}.
74   * Default value is {@code 0}.
75   * </li>
76   * </ul>
77   * <p>
78   * Parent is {@code com.puppycrawl.tools.checkstyle.Checker}
79   * </p>
80   * <p>
81   * Violation Message Keys:
82   * </p>
83   * <ul>
84   * <li>
85   * {@code regexp.StackOverflowError}
86   * </li>
87   * <li>
88   * {@code regexp.empty}
89   * </li>
90   * <li>
91   * {@code regexp.exceeded}
92   * </li>
93   * <li>
94   * {@code regexp.minimum}
95   * </li>
96   * </ul>
97   *
98   * @since 5.0
99   */
100 @StatelessCheck
101 public 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 }