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.util.regex.Matcher;
23  
24  import com.puppycrawl.tools.checkstyle.api.FileText;
25  import com.puppycrawl.tools.checkstyle.api.LineColumn;
26  
27  /**
28   * A detector that matches across multiple lines.
29   */
30  public class MultilineDetector {
31  
32      /** The detection options to use. */
33      private final DetectorOptions options;
34      /** The message key for exceeded matches. */
35      private final String exceededMessage;
36      /** The message key for minimum matches not met. */
37      private final String minimumMessage;
38      /** The message key for empty format. */
39      private final String emptyMessage;
40      /** The message key for StackOverflow error. */
41      private final String stackOverflowMessage;
42      /** Tracks the number of matches. */
43      private int currentMatches;
44      /** The matcher. */
45      private Matcher matcher;
46      /** The file text content. */
47      private FileText text;
48  
49      /**
50       * Creates an instance.
51       *
52       * @param options the options to use.
53       * @param exceededMessage the message key for exceeded matches.
54       * @param minimumMessage the message key for minimum matches not met.
55       * @param emptyMessage the message key for empty format.
56       * @param stackOverflowMessage the message key for StackOverflow error.
57       */
58      /* package */ MultilineDetector(DetectorOptions options,
59              String exceededMessage, String minimumMessage,
60              String emptyMessage, String stackOverflowMessage) {
61          this.options = options;
62          this.exceededMessage = exceededMessage;
63          this.minimumMessage = minimumMessage;
64          this.emptyMessage = emptyMessage;
65          this.stackOverflowMessage = stackOverflowMessage;
66      }
67  
68      /**
69       * Processes an entire text file looking for matches.
70       *
71       * @param fileText the text to process
72       */
73      public void processLines(FileText fileText) {
74          text = new FileText(fileText);
75          resetState();
76  
77          final String format = options.getFormat();
78          if (format == null || format.isEmpty()) {
79              options.getReporter().log(1, emptyMessage);
80          }
81          else {
82              matcher = options.getPattern().matcher(fileText.getFullText());
83              findMatch();
84              finish();
85          }
86      }
87  
88      /** Method that finds the matches. */
89      private void findMatch() {
90          try {
91              boolean foundMatch = matcher.find();
92  
93              while (foundMatch) {
94                  currentMatches++;
95                  if (currentMatches > options.getMaximum()) {
96                      final LineColumn start = text.lineColumn(matcher.start());
97                      if (options.getMessage().isEmpty()) {
98                          options.getReporter().log(start.getLine(),
99                                  exceededMessage,
100                                         matcher.pattern().toString());
101                     }
102                     else {
103                         options.getReporter()
104                                 .log(start.getLine(), options.getMessage());
105                     }
106                 }
107                 foundMatch = matcher.find();
108             }
109         }
110         // see http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993 et al.
111         catch (StackOverflowError ignored) {
112             // ok http://blog.igorminar.com/2008/05/catching-stackoverflowerror-and-bug-in.html
113             // http://programmers.stackexchange.com/questions/
114             //        209099/is-it-ever-okay-to-catch-stackoverflowerror-in-java
115             options.getReporter().log(1, stackOverflowMessage,
116                         matcher.pattern().toString());
117         }
118     }
119 
120     /** Perform processing at the end of a set of lines. */
121     private void finish() {
122         if (currentMatches < options.getMinimum()) {
123             if (options.getMessage().isEmpty()) {
124                 options.getReporter().log(1, minimumMessage,
125                         options.getMinimum(), options.getFormat());
126             }
127             else {
128                 options.getReporter().log(1, options.getMessage());
129             }
130         }
131     }
132 
133     /**
134      * Reset the state of the detector.
135      */
136     private void resetState() {
137         currentMatches = 0;
138     }
139 
140 }