Skip to content

Commit

Permalink
Regex pair bug (#3)
Browse files Browse the repository at this point in the history
Fix bug in capturing paired regex groups
  • Loading branch information
kdebisschop authored Jan 22, 2020
1 parent 88ba8a8 commit da924d0
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -34,6 +33,8 @@
import com.dtolabs.rundeck.plugins.step.NodeStepPlugin;
import com.dtolabs.rundeck.plugins.step.PluginStepContext;

import static com.dtolabs.rundeck.core.Constants.DEBUG_LEVEL;

/**
* Workflow Step Plug-in to find value of first matching text file.
*
Expand Down Expand Up @@ -61,55 +62,62 @@ public class ScanFileNodeStepPlugin implements NodeStepPlugin {
@PluginProperty(title = "Group", description = "Variable group (i.e., ${group.x}}", required = true, defaultValue = "data")
private String group;

@PluginProperty(title = "Name", description = "Variable name (i.e., ${group.name}) [ignored when Pattern has 2 capture fields]", required = false)
@PluginProperty(title = "Name", description = "Variable name (i.e., ${group.name}) [ignored when Pattern has 2 capture fields]")
private String name;

@PluginProperty(title = "Pattern", description = "Regular expression to find, with one or two capture fields", required = true)
private String regex;

@PluginProperty(title = "Make global?", description = "Elevate this variable to global scope (default: false)", required = false)
@PluginProperty(title = "Make global?", description = "Elevate this variable to global scope (default: false)", required = true, defaultValue = "false")
private boolean elevateToGlobal;

@Override
public void executeNodeStep(PluginStepContext context, Map<String, Object> configuration, INodeEntry node)
throws NodeStepException {
String path = configuration.getOrDefault("path", this.path).toString();
String group = configuration.getOrDefault("group", this.group).toString();
String name = configuration.getOrDefault("name", this.name).toString();
String regex = configuration.getOrDefault("regex", this.regex).toString();
boolean elevateToGlobal = configuration.getOrDefault("elevateToGlobal", this.elevateToGlobal).toString()
.equals("true");

Pattern pattern = Pattern.compile(regex);
MatchResult match;
Matcher matcher;
String line;
path = configuration.getOrDefault("path", this.path).toString();
group = configuration.getOrDefault("group", this.group).toString();
regex = configuration.getOrDefault("regex", this.regex).toString();
if (configuration.containsKey("elevateToGlobal")) {
elevateToGlobal = configuration.get("elevateToGlobal").toString().equals("true");
}

if (name == null || name.equals("")) {
name = "data";
// Setting name is different because it is not a required field.
if (name == null || name.length() == 0) {
name = configuration.getOrDefault("name", "").toString();
if (name == null || name.length() == 0) {
name = "data";
}
}

Pattern pattern = Pattern.compile(regex);

Map<String, String> map = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
// Scan lines for a match.
// Optimize by returning immediately when there is only one capture field.
while ((line = reader.readLine()) != null) {
matcher = pattern.matcher(line);
if (matcher.find()) {
match = matcher.toMatchResult();
do {
String line = reader.readLine();
if (line == null) {
return;
}
Matcher match = pattern.matcher(line);
if (match.find()) {
context.getLogger().log(DEBUG_LEVEL, "Matched " + line);
if (match.groupCount() == 1) {
FileLookupUtils.addOutput(context, group, name, match.group(1), elevateToGlobal);
return;
} else if (match.groupCount() == 2) {
context.getLogger().log(DEBUG_LEVEL, "Found '" + match.group(1) + "' : '" + match.group(2) + "'");
// Take first value and do not overwrite, even though scanning proceeds
// through the rest of the file to find other matches to the pattern.
if (map.get(match.group(1)) == null) {
if (!map.containsKey(match.group(1))) {
FileLookupUtils.addOutput(context, group, match.group(1), match.group(2), elevateToGlobal);
map.put(match.group(1), match.group(2));
}
}
}
}
} while (true);
} catch (FileNotFoundException e) {
String msg = "Could not find file " + path;
String nodeName = node.getNodename();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -33,6 +32,8 @@
import com.dtolabs.rundeck.plugins.step.PluginStepContext;
import com.dtolabs.rundeck.plugins.step.StepPlugin;

import static com.dtolabs.rundeck.core.Constants.DEBUG_LEVEL;

/**
* Workflow Step Plug-in to find value of first matching text file.
*
Expand Down Expand Up @@ -60,60 +61,67 @@ public class ScanFileStepPlugin implements StepPlugin {
@PluginProperty(title = "Group", description = "Variable group (i.e., ${group.x}}", required = true, defaultValue = "data")
private String group;

@PluginProperty(title = "Name", description = "Variable name (i.e., ${group.name}) [ignored when Pattern has 2 capture fields]", required = false)
@PluginProperty(title = "Name", description = "Variable name (i.e., ${group.name}) [ignored when Pattern has 2 capture fields]")
private String name;

@PluginProperty(title = "Pattern", description = "Regular expression to find, with one or two capture fields", required = true)
private String regex;

@PluginProperty(title = "Make global?", description = "Elevate this variable to global scope (default: false)", required = false)
@PluginProperty(title = "Make global?", description = "Elevate this variable to global scope (default: false)", required = true, defaultValue = "false")
private boolean elevateToGlobal;

@Override
public void executeStep(PluginStepContext context, Map<String, Object> configuration) throws StepException {
String path = configuration.getOrDefault("path", this.path).toString();
String group = configuration.getOrDefault("group", this.group).toString();
String name = configuration.getOrDefault("name", this.name).toString();
String regex = configuration.getOrDefault("regex", this.regex).toString();
boolean elevateToGlobal = configuration.getOrDefault("elevateToGlobal", this.elevateToGlobal).toString()
.equals("true");

Pattern pattern = Pattern.compile(regex);
MatchResult match;
Matcher matcher;
String line;
path = configuration.getOrDefault("path", this.path).toString();
group = configuration.getOrDefault("group", this.group).toString();
regex = configuration.getOrDefault("regex", this.regex).toString();
if (configuration.containsKey("elevateToGlobal")) {
elevateToGlobal = configuration.get("elevateToGlobal").toString().equals("true");
}

if (name == null || name.equals("")) {
name = "data";
// Setting name is different because it is not a required field.
if (name == null || name.length() == 0) {
name = configuration.getOrDefault("name", "").toString();
if (name == null || name.length() == 0) {
name = "data";
}
}

Pattern pattern = Pattern.compile(regex);

Map<String, String> map = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
// Scan lines for a match.
// Optimize by returning immediately when there is only one capture field.
while ((line = reader.readLine()) != null) {
matcher = pattern.matcher(line);
if (matcher.find()) {
match = matcher.toMatchResult();
do {
String line = reader.readLine();
if (line == null) {
return;
}
Matcher match = pattern.matcher(line);
if (match.find()) {
context.getLogger().log(DEBUG_LEVEL, "Matched " + line);
if (match.groupCount() == 1) {
FileLookupUtils.addOutput(context, group, name, match.group(1), elevateToGlobal);
return;
} else if (match.groupCount() == 2) {
if (map.get(match.group(1)) == null) {
context.getLogger().log(DEBUG_LEVEL, "Found '" + match.group(1) + "' : '" + match.group(2) + "'");
// Take first value and do not overwrite, even though scanning proceeds
// through the rest of the file to find other matches to the pattern.
if (!map.containsKey(match.group(1))) {
FileLookupUtils.addOutput(context, group, match.group(1), match.group(2), elevateToGlobal);
map.put(match.group(1), match.group(2));
}
}
}
}
} while (true);
} catch (FileNotFoundException e) {
String msg = "Could not find file " + path;
throw new StepException(msg, e, FileLookupFailureReason.FileNotFound);
} catch (IOException e) {
String msg = "Could not read file " + path;
throw new StepException(msg, e, FileLookupFailureReason.FileNotReadable);
}
for (Map.Entry<String, String> element : map.entrySet()) {
FileLookupUtils.addOutput(context, group, element.getKey(), element.getValue(), elevateToGlobal);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.dtolabs.rundeck.plugins.PluginLogger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -62,6 +63,9 @@ public class ScanFileNodeStepPluginTest {
@Mock
SharedOutputContext sharedOutputContext;

@Mock
PluginLogger logger;

@Captor
ArgumentCaptor<String> nameCaptor;

Expand All @@ -76,6 +80,8 @@ public void setUp() {
configuration = Stream.of(new String[][] { { "path", "testData/test.yaml" }, { "group", "example" },
{ "name", "key" }, { "regex", "single" }, })
.collect(Collectors.toMap(data -> data[0], data -> data[1]));
when(context.getLogger()).thenReturn(logger);

}

@Test(expected = StepException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
package com.bioraft.rundeck.filelookup;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.matches;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand All @@ -29,6 +28,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.dtolabs.rundeck.plugins.PluginLogger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -58,6 +58,9 @@ public class ScanFileStepPluginTest {
@Mock
SharedOutputContext sharedOutputContext;

@Mock
PluginLogger logger;

@Captor
ArgumentCaptor<String> nameCaptor;

Expand All @@ -72,6 +75,7 @@ public void setUp() {
configuration = Stream.of(new String[][] { { "path", "testData/test.yaml" }, { "group", "example" },
{ "name", "key" }, { "regex", "single" }, })
.collect(Collectors.toMap(data -> data[0], data -> data[1]));
when(context.getLogger()).thenReturn(logger);
}

@Test(expected = StepException.class)
Expand All @@ -83,7 +87,7 @@ public void noFileThrowsException() throws StepException {

@Test
public void canRunWithoutMatch() throws StepException {
configuration.put("regex", "com[.]example[.]label3: (.*)");
configuration.put("regex", "^\\s+com[.]example[.]label3: (.*)");
when(context.getOutputContext()).thenReturn(sharedOutputContext);

this.plugin.executeStep(context, configuration);
Expand All @@ -110,7 +114,7 @@ public void canFindSingleCapture() throws StepException {

@Test
public void canFindMultipleCapture() throws StepException {
configuration.put("regex", "com[.]example[.](label1|label2): (.*)");
configuration.put("regex", "^\\s+com[.]example[.](label1|label2): (.*)");
when(context.getOutputContext()).thenReturn(sharedOutputContext);

this.plugin.executeStep(context, configuration);
Expand Down

0 comments on commit da924d0

Please sign in to comment.