001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.regexp; 021 022import java.util.regex.Matcher; 023 024import com.puppycrawl.tools.checkstyle.api.FileText; 025import com.puppycrawl.tools.checkstyle.api.LineColumn; 026 027/** 028 * A detector that matches across multiple lines. 029 */ 030public class MultilineDetector { 031 032 /** 033 * A key is pointing to the warning message text in "messages.properties" 034 * file. 035 */ 036 public static final String MSG_REGEXP_EXCEEDED = "regexp.exceeded"; 037 038 /** 039 * A key is pointing to the warning message text in "messages.properties" 040 * file. 041 */ 042 public static final String MSG_REGEXP_MINIMUM = "regexp.minimum"; 043 044 /** 045 * A key is pointing to the warning message text in "messages.properties" 046 * file. 047 */ 048 public static final String MSG_EMPTY = "regexp.empty"; 049 /** 050 * A key is pointing to the warning message text in "messages.properties" 051 * file. 052 */ 053 public static final String MSG_STACKOVERFLOW = "regexp.StackOverflowError"; 054 055 /** The detection options to use. */ 056 private final DetectorOptions options; 057 /** Tracks the number of matches. */ 058 private int currentMatches; 059 /** The matcher. */ 060 private Matcher matcher; 061 /** The file text content. */ 062 private FileText text; 063 064 /** 065 * Creates an instance. 066 * 067 * @param options the options to use. 068 */ 069 /* package */ MultilineDetector(DetectorOptions options) { 070 this.options = options; 071 } 072 073 /** 074 * Processes an entire text file looking for matches. 075 * 076 * @param fileText the text to process 077 */ 078 public void processLines(FileText fileText) { 079 text = new FileText(fileText); 080 resetState(); 081 082 final String format = options.getFormat(); 083 if (format == null || format.isEmpty()) { 084 options.getReporter().log(1, MSG_EMPTY); 085 } 086 else { 087 matcher = options.getPattern().matcher(fileText.getFullText()); 088 findMatch(); 089 finish(); 090 } 091 } 092 093 /** Method that finds the matches. */ 094 private void findMatch() { 095 try { 096 boolean foundMatch = matcher.find(); 097 098 while (foundMatch) { 099 currentMatches++; 100 if (currentMatches > options.getMaximum()) { 101 final int reportGroupValue = options.getReportGroup(); 102 final int violationStartIndex; 103 104 if (reportGroupValue <= matcher.groupCount() 105 && matcher.start(reportGroupValue) != -1) { 106 violationStartIndex = matcher.start(reportGroupValue); 107 } 108 else { 109 violationStartIndex = matcher.start(); 110 } 111 112 final LineColumn start = text.lineColumn(violationStartIndex); 113 114 if (options.getMessage().isEmpty()) { 115 options.getReporter().log(start.getLine(), 116 MSG_REGEXP_EXCEEDED, matcher.pattern().toString()); 117 } 118 else { 119 options.getReporter() 120 .log(start.getLine(), options.getMessage()); 121 } 122 } 123 foundMatch = matcher.find(); 124 } 125 } 126 // see http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993 et al. 127 catch (StackOverflowError ignored) { 128 // ok http://blog.igorminar.com/2008/05/catching-stackoverflowerror-and-bug-in.html 129 // http://programmers.stackexchange.com/questions/ 130 // 209099/is-it-ever-okay-to-catch-stackoverflowerror-in-java 131 options.getReporter().log(1, MSG_STACKOVERFLOW, matcher.pattern().toString()); 132 } 133 } 134 135 /** Perform processing at the end of a set of lines. */ 136 private void finish() { 137 if (currentMatches < options.getMinimum()) { 138 if (options.getMessage().isEmpty()) { 139 options.getReporter().log(1, MSG_REGEXP_MINIMUM, 140 options.getMinimum(), options.getFormat()); 141 } 142 else { 143 options.getReporter().log(1, options.getMessage()); 144 } 145 } 146 } 147 148 /** 149 * Reset the state of the detector. 150 */ 151 private void resetState() { 152 currentMatches = 0; 153 } 154 155}