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