1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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       * @param options the options to use.
51       */
52      /* package */ SinglelineDetector(DetectorOptions options) {
53          this.options = options;
54      }
55  
56      /**
57       * Processes a set of lines looking for matches.
58       * @param fileText {@link FileText} object contains the lines to process.
59       */
60      public void processLines(FileText fileText) {
61          resetState();
62          int lineNo = 0;
63          for (int index = 0; index < fileText.size(); index++) {
64              final String line = fileText.get(index);
65              lineNo++;
66              checkLine(lineNo, line, options.getPattern().matcher(line), 0);
67          }
68          finish();
69      }
70  
71      /** Perform processing at the end of a set of lines. */
72      private void finish() {
73          if (currentMatches < options.getMinimum()) {
74              if (options.getMessage().isEmpty()) {
75                  options.getReporter().log(1, MSG_REGEXP_MINIMUM,
76                          options.getMinimum(), options.getFormat());
77              }
78              else {
79                  options.getReporter().log(1, options.getMessage());
80              }
81          }
82      }
83  
84      /**
85       * Reset the state of the detector.
86       */
87      private void resetState() {
88          currentMatches = 0;
89      }
90  
91      /**
92       * Check a line for matches.
93       * @param lineNo the line number of the line to check
94       * @param line the line to check
95       * @param matcher the matcher to use
96       * @param startPosition the position to start searching from.
97       */
98      private void checkLine(int lineNo, String line, Matcher matcher,
99              int startPosition) {
100         final boolean foundMatch = matcher.find(startPosition);
101         if (foundMatch) {
102             // match is found, check for intersection with comment
103             final int startCol = matcher.start(0);
104             final int endCol = matcher.end(0);
105             // Note that Matcher.end(int) returns the offset AFTER the
106             // last matched character, but shouldSuppress()
107             // needs column number of the last character.
108             // So we need to use (endCol - 1) here.
109             if (options.getSuppressor()
110                     .shouldSuppress(lineNo, startCol, lineNo, endCol - 1)) {
111                 checkLine(lineNo, line, matcher, endCol);
112             }
113             else {
114                 currentMatches++;
115                 if (currentMatches > options.getMaximum()) {
116                     if (options.getMessage().isEmpty()) {
117                         options.getReporter().log(lineNo, MSG_REGEXP_EXCEEDED,
118                                 matcher.pattern().toString());
119                     }
120                     else {
121                         options.getReporter().log(lineNo, options.getMessage());
122                     }
123                 }
124             }
125         }
126     }
127 
128 }