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.util.regex.Matcher;
23  
24  import com.puppycrawl.tools.checkstyle.api.FileText;
25  
26  /**
27   * A detector that matches individual lines.
28   */
29  class SinglelineDetector {
30  
31      /**
32       * A key is pointing to the warning message text in "messages.properties"
33       * file.
34       */
35      public static final String MSG_REGEXP_EXCEEDED = "regexp.exceeded";
36  
37      /**
38       * A key is pointing to the warning message text in "messages.properties"
39       * file.
40       */
41      public static final String MSG_REGEXP_MINIMUM = "regexp.minimum";
42  
43      /** The detection options to use. */
44      private final DetectorOptions options;
45      /** Tracks the number of matches. */
46      private int currentMatches;
47  
48      /**
49       * Creates an instance.
50       *
51       * @param options the options to use.
52       */
53      /* package */ SinglelineDetector(DetectorOptions options) {
54          this.options = options;
55      }
56  
57      /**
58       * Processes a set of lines looking for matches.
59       *
60       * @param fileText {@link FileText} object contains the lines to process.
61       */
62      public void processLines(FileText fileText) {
63          resetState();
64          int lineNo = 0;
65          for (int index = 0; index < fileText.size(); index++) {
66              final String line = fileText.get(index);
67              lineNo++;
68              checkLine(lineNo, line, options.getPattern().matcher(line), 0);
69          }
70          finish();
71      }
72  
73      /** Perform processing at the end of a set of lines. */
74      private void finish() {
75          if (currentMatches < options.getMinimum()) {
76              if (options.getMessage().isEmpty()) {
77                  options.getReporter().log(1, MSG_REGEXP_MINIMUM,
78                          options.getMinimum(), options.getFormat());
79              }
80              else {
81                  options.getReporter().log(1, options.getMessage());
82              }
83          }
84      }
85  
86      /**
87       * Reset the state of the detector.
88       */
89      private void resetState() {
90          currentMatches = 0;
91      }
92  
93      /**
94       * Check a line for matches.
95       *
96       * @param lineNo the line number of the line to check
97       * @param line the line to check
98       * @param matcher the matcher to use
99       * @param startPosition the position to start searching from.
100      * @noinspection TailRecursion
101      * @noinspectionreason TailRecursion - until issue #14814
102      */
103     private void checkLine(int lineNo, String line, Matcher matcher,
104             int startPosition) {
105         final boolean foundMatch = matcher.find(startPosition);
106         if (foundMatch) {
107             // match is found, check for intersection with comment
108             final int startCol = matcher.start(0);
109             final int endCol = matcher.end(0);
110             // Note that Matcher.end(int) returns the offset AFTER the
111             // last matched character, but shouldSuppress()
112             // needs column number of the last character.
113             // So we need to use (endCol - 1) here.
114             if (options.getSuppressor()
115                     .shouldSuppress(lineNo, startCol, lineNo, endCol - 1)) {
116                 checkLine(lineNo, line, matcher, endCol);
117             }
118             else {
119                 currentMatches++;
120                 if (currentMatches > options.getMaximum()) {
121                     if (options.getMessage().isEmpty()) {
122                         options.getReporter().log(lineNo, MSG_REGEXP_EXCEEDED,
123                                 matcher.pattern().toString());
124                     }
125                     else {
126                         options.getReporter().log(lineNo, options.getMessage());
127                     }
128                 }
129             }
130         }
131     }
132 
133 }