Skip to content

Commit

Permalink
Issue #6717: add support to RegexpMultiline to match across lines
Browse files Browse the repository at this point in the history
  • Loading branch information
rnveach authored and romani committed Sep 28, 2019
1 parent 638f601 commit 6bc999d
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class RegexpMultilineCheck extends AbstractFileSetCheck {
private int maximum;
/** Whether to ignore case when matching. */
private boolean ignoreCase;
/** Whether to match across multiple lines. */
private boolean matchAcrossLines;

/** The detector to use. */
private MultilineDetector detector;
Expand All @@ -51,7 +53,7 @@ public class RegexpMultilineCheck extends AbstractFileSetCheck {
public void beginProcessing(String charset) {
final DetectorOptions options = DetectorOptions.newBuilder()
.reporter(this)
.compileFlags(Pattern.MULTILINE)
.compileFlags(getRegexCompileFlags())
.format(format)
.message(message)
.minimum(minimum)
Expand All @@ -66,6 +68,24 @@ protected void processFiltered(File file, FileText fileText) {
detector.processLines(fileText);
}

/**
* Retrieves the compile flags for the regular expression being built based
* on {@code matchAcrossLines}.
* @return The compile flags.
*/
private int getRegexCompileFlags() {
final int result;

if (matchAcrossLines) {
result = Pattern.DOTALL;
}
else {
result = Pattern.MULTILINE;
}

return result;
}

/**
* Sets the format of the regular expression to match.
* @param format the format of the regular expression to match.
Expand Down Expand Up @@ -106,4 +126,12 @@ public void setIgnoreCase(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
}

/**
* Sets whether to match across multiple lines.
* @param matchAcrossLines whether to match across multiple lines.
*/
public void setMatchAcrossLines(boolean matchAcrossLines) {
this.matchAcrossLines = matchAcrossLines;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,27 @@ public void testGoodLimit() throws Exception {
verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
}

@Test
public void testMultilineSupport() throws Exception {
final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
checkConfig.addAttribute("format", "abc.*def");
checkConfig.addAttribute("matchAcrossLines", "true");
final String[] expected = {
"9: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "abc.*def"),
};
verify(checkConfig, getPath("InputRegexpMultilineMultilineSupport.java"), expected);
}

@Test
public void testMultilineSupportNotGreedy() throws Exception {
final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
checkConfig.addAttribute("format", "abc.*?def");
checkConfig.addAttribute("matchAcrossLines", "true");
final String[] expected = {
"9: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "abc.*?def"),
"11: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "abc.*?def"),
};
verify(checkConfig, getPath("InputRegexpMultilineMultilineSupport2.java"), expected);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.puppycrawl.tools.checkstyle.checks.regexp.regexpmultiline;

/**
* Config format = 'ABC.*DEF'(lowercased)
* matchAcrossLines = true
*/
public class InputRegexpMultilineMultilineSupport {
void method() {
// abc - violation
// def
// abc
}

void method2() {
// def
// abc
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.puppycrawl.tools.checkstyle.checks.regexp.regexpmultiline;

/**
* Config format = 'ABC.*?DEF'(lowercased)
* matchAcrossLines = true
*/
public class InputRegexpMultilineMultilineSupport2 {
void method() {
// abc - violation
// def
// abc - violation
}

void method2() {
// def
// abc
}
}
37 changes: 37 additions & 0 deletions src/xdocs/config_regexp.xml
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,13 @@ public static final int A_SETPOINT = 1;
<td><code>0</code></td>
<td>5.0</td>
</tr>
<tr>
<td>matchAcrossLines</td>
<td>Controls whether to match expressions across multiple lines.</td>
<td><a href="property_types.html#boolean">Boolean</a></td>
<td><code>false</code></td>
<td>8.25</td>
</tr>
<tr>
<td>fileExtensions</td>
<td>file type extension of files to process</td>
Expand All @@ -570,6 +577,36 @@ public static final int A_SETPOINT = 1;
value=&quot;System\.(out)|(err)\.print(ln)?\(&quot;/&gt;
&lt;/module&gt;
</source>

<p>
To configure the check to match text that spans multiple lines,
like normal code in a Java file:
</p>
<source>
&lt;module name=&quot;RegexpMultiline&quot;&gt;
&lt;property name=&quot;matchAcrossLines&quot; value=&quot;true&quot;/&gt;
&lt;property name=&quot;format&quot; value=&quot;System\.out.*print\(&quot;/&gt;
&lt;/module&gt;
</source>
<p>
Example of violation from the above config:
</p>
<source>
void method() {
System.out. // violation
print("Example");
System.out.
print("Example");
}
</source>
<p>
Note: Beware of the greedy regular expression used in the above example.
<code>.*</code> will match as much as possible and not produce multiple violations in
the file if multiple groups of lines could match the expression. To prevent an
expression being too greedy, avoid overusing matching all text or allow it to be
optional, like <code>.*?</code>. Changing the example expression to not be greedy
will allow multiple violations in the example to be found in the same file.
</p>
</subsection>

<subsection name="Example of Usage" id="RegexpMultiline_Example_of_Usage">
Expand Down

0 comments on commit 6bc999d

Please sign in to comment.