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.io.File; 023import java.util.regex.Pattern; 024 025import com.puppycrawl.tools.checkstyle.PropertyType; 026import com.puppycrawl.tools.checkstyle.StatelessCheck; 027import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 028import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 029import com.puppycrawl.tools.checkstyle.api.FileText; 030 031/** 032 * <div> 033 * Checks that a specified pattern matches across multiple lines in any file type. 034 * </div> 035 * 036 * <p> 037 * Rationale: This check can be used to when the regular expression can be span multiple lines. 038 * </p> 039 * 040 * @since 5.0 041 */ 042@StatelessCheck 043public class RegexpMultilineCheck extends AbstractFileSetCheck { 044 045 /** Specify the format of the regular expression to match. */ 046 @XdocsPropertyType(PropertyType.PATTERN) 047 private String format = "$."; 048 /** 049 * Specify the message which is used to notify about violations, 050 * if empty then default (hard-coded) message is used. 051 */ 052 private String message; 053 /** Specify the minimum number of matches required in each file. */ 054 private int minimum; 055 /** Specify the maximum number of matches required in each file. */ 056 private int maximum; 057 /** Control whether to ignore case when searching. */ 058 private boolean ignoreCase; 059 /** Control whether to match expressions across multiple lines. */ 060 private boolean matchAcrossLines; 061 /** Specify which capturing group to use for violation reporting. */ 062 private int reportGroup; 063 064 /** The detector to use. */ 065 private MultilineDetector detector; 066 067 @Override 068 public void beginProcessing(String charset) { 069 final DetectorOptions options = DetectorOptions.newBuilder() 070 .reporter(this) 071 .compileFlags(getRegexCompileFlags()) 072 .format(format) 073 .message(message) 074 .minimum(minimum) 075 .maximum(maximum) 076 .ignoreCase(ignoreCase) 077 .reportGroup(reportGroup) 078 .build(); 079 detector = new MultilineDetector(options); 080 } 081 082 @Override 083 protected void processFiltered(File file, FileText fileText) { 084 detector.processLines(fileText); 085 } 086 087 /** 088 * Retrieves the compile-flags for the regular expression being built based 089 * on {@code matchAcrossLines}. 090 * 091 * @return The compile-flags. 092 */ 093 private int getRegexCompileFlags() { 094 final int result; 095 096 if (matchAcrossLines) { 097 result = Pattern.DOTALL; 098 } 099 else { 100 result = Pattern.MULTILINE; 101 } 102 103 return result; 104 } 105 106 /** 107 * Setter to specify the format of the regular expression to match. 108 * 109 * @param format the format of the regular expression to match. 110 * @since 5.0 111 */ 112 public void setFormat(String format) { 113 this.format = format; 114 } 115 116 /** 117 * Setter to specify the message which is used to notify about violations, 118 * if empty then default (hard-coded) message is used. 119 * 120 * @param message the message to report for a match. 121 * @since 5.0 122 */ 123 public void setMessage(String message) { 124 this.message = message; 125 } 126 127 /** 128 * Setter to specify the minimum number of matches required in each file. 129 * 130 * @param minimum the minimum number of matches required in each file. 131 * @since 5.0 132 */ 133 public void setMinimum(int minimum) { 134 this.minimum = minimum; 135 } 136 137 /** 138 * Setter to specify the maximum number of matches required in each file. 139 * 140 * @param maximum the maximum number of matches required in each file. 141 * @since 5.0 142 */ 143 public void setMaximum(int maximum) { 144 this.maximum = maximum; 145 } 146 147 /** 148 * Setter to control whether to ignore case when searching. 149 * 150 * @param ignoreCase whether to ignore case when searching. 151 * @since 5.0 152 */ 153 public void setIgnoreCase(boolean ignoreCase) { 154 this.ignoreCase = ignoreCase; 155 } 156 157 /** 158 * Setter to control whether to match expressions across multiple lines. 159 * 160 * @param matchAcrossLines whether to match expressions across multiple lines. 161 * @since 8.25 162 */ 163 public void setMatchAcrossLines(boolean matchAcrossLines) { 164 this.matchAcrossLines = matchAcrossLines; 165 } 166 167 /** 168 * Setter to specify which capturing group to use for violation reporting. 169 * 170 * @param reportGroup the capturing group number to use (0 for entire match). 171 * @since 13.3.0 172 */ 173 public void setReportGroup(int reportGroup) { 174 this.reportGroup = reportGroup; 175 } 176 177}